(C) pointers passed to func differ from original - c

Segfault I find myself unable to diagnose.
I have a benchmark func chrono which receives an array strings of pointers to randomly generated strings, and an array of corresponding sizes. For debug, I print strings (the pointer to pointers), strings[1] (one of them), and sizes (the pointer). (I also print some strings in chrono, to be sure, and compare to their original contents in the func that generates them: all is fine up to there.) What is to be benchmarked is a func that feeds a pack of strings into a string pool:
void pool_store_pack (StringPool * pool,
char ** strings, size_t * sizes, uint n_new);
For debug, I print the same data inside pool_store_pack. And the values are different. strings has changed, and both strings[1] and sizes are null. Here is an example output:
strings:0x9842fa8 (n°1:0x984200c)
sizes:0x9843f48
some strings: `#PR` `MOB` `TBQ`
strings:0x804a824 (n°1:(nil))
sizes:(nil)
segmentation fault (core dumped)
I have tried to reduce the bug to a simpler form, but it is difficult precisely because it is test code, not app code, that drives an autonomous piece of library. When I try to reproduce the bug from scratch, I just get as expected pointers and pointers to pointers with the same values in the receiving funcs as in the sending one. However, I can post the code of the 3 relevant funcs if it helps (but you won`t be able to run it, since it just drives other funcs).
I am pretty sure to be missing an obvious point, but cannot see it and am blocked and frustrated for hours ;-) Can you help?
EDIT: So, here is the whole code involved:
/* Put pack of strings in pool.
*/
void pool_store_pack (StringPool * pool,
char ** strings, size_t * sizes, uint n_new) {
pool_grow(pool, n_new);
// debug /////////////////////////////////////////////////////////
printfl("n_new:%u", n_new);
printfl("strings:%p (n°1:%p)", strings, strings[1]);
printfl("sizes:%p", sizes);
printfl("some sizes: %u %u %u", sizes[1], sizes[3], sizes[9]);
printfl("some strings: '%s' '%s' '%s'", strings[1], strings[3], strings[9]);
end();
uint i;
for (i = 0; i < n_new; i++) pool_store(pool, strings[i], sizes[i]);
}
// generate random strings (constants used here are defined somewhere else)
static char ** data_strings (char ** p_bytes) {
char * bytes = malloc(n_strings * l * sizeof(char));
check_mem(bytes);
char ** strings = malloc(n_strings * sizeof(char *));
check_mem(strings);
char * s;
uint i,j;
srandom(time(NULL));
for (i=0; i<n_strings; i++) {
s = bytes + i*l;
s[size] = NUL;
for (j=0; j<size; j++) s[j] = '#' + random()%n_letters;
strings[i] = s;
}
//~ for (i=0; i<n_strings; i++) printf("'%s' ", strings[i]); line();
printfl("some strings: '%s' '%s' '%s'", strings[1], strings[3], strings[9]);
* p_bytes = bytes;
return strings;
}
// benchmark
static void chrono () {
printfl ("=== chrono pool ===============================================");
uint i;
clock_t t1, t2;
float dt;
// test data string
char * bytes;
char ** strings = data_strings(& bytes);
// string sizes are all equal to size (3)
size_t * sizes = malloc(n_strings * sizeof(size_t));
check_mem(sizes);
for (i=0; i<n_strings; i++) sizes[i] = size;
// debug ///////////////////////////////////////////////////////////////////
printfl("n_strings:%u", n_strings);
printfl("strings:%p (n°1:%p)", strings, strings[1]);
printfl("sizes:%p", sizes);
printfl("some sizes: %u %u %u", sizes[1], sizes[3], sizes[9]);
printfl("some strings: '%s' '%s' '%s'", strings[1], strings[3], strings[9]);
// now, feed the pool
StringPool * pool = stringpool();
t1 = clock();
pool_store_pack(pool, strings, sizes, n_strings);
t2 = clock();
end();
dt = 1.0 * (t2 - t1) / CLOCKS_PER_SEC;
print("pool data : "); show_pool_data(pool);
print("pool stats : "); show_pool_stats(pool, false);
printfl("time : %.3f", dt);
free(bytes);
free(strings);
}

OK runs! There were 2 bugs in fact, interacting with each other. First, a wrong redefinition of a static constant giving the sizes of an initial set of strings to feed the pool (in fact only "", so this set has length 1). Second, an inversion of 2 instructions making the pool beeing used before initialised. The interaction is that the initialisation also used pool_store_pack to feed the pool with said initial strings.
==>
What I got as debug output (and showed you) was reflecting the data from calling this func pool_store_pack for pool initialisation, not from the chrono func (thus, strings[1] was nil because there is only one string in the initial set!). And sizes was nil because it is the static constant wrongly changed... Mama mia!
Thanks to you all! Unfortunately I cannot vote you up, since there are only comments...

Related

How does this array size come about?

I have managed to produced a code that works perfectly fine and does exactly what it should. My problem is: I absolutely don't understand why. In my understanding it should NOT work.
#define PARAMS 12
char** parseFile(char* tmp[], char* location) {
// Parse a file at <location> and retrieve 6 key: value pairs from it.
// All key: value pairs are stored in tmp, resulting in an array of
// 12 char pointers, each pointing to a char array that contains either
// a key or a value.
// ...
do {
yaml_parser_scan(&parser, &token);
if (token.type == YAML_SCALAR_TOKEN && i<PARAMS) {
strcpy(tmp[i], (char*)token.data.scalar.value);
i++;
}
if (token.type != YAML_STREAM_END_TOKEN) {
yaml_token_delete(&token);
}
} while (token.type != YAML_STREAM_END_TOKEN);
// ...
return tmp;
}
int main(int argc, char* argv[]) {
int i=0;
char **tmp = (char **)calloc(PARAMS, sizeof(char *));
char **params = (char **)calloc(PARAMS, sizeof(char *));
for (i=0; i<PARAMS; i++) {
tmp[i] = (char *)calloc(32, sizeof(char));
params[i] = (char *)calloc(32, sizeof(char));
}
memcpy(params, parseFile(tmp, argv[1]), sizeof(char) * 56); // WHY 56?
for (i=0; i<PARAMS; i++) {
printf("PARAM_[%d] = %s\n", i, params[i]);
}
free(tmp);
free(params);
return 0;
}
I could swear that I would have to specify a size of sizeof(char) * 384 (1 * 12 * 32) in memcpy for the code to work. But if I do, it doesn't. It only works and produces the correct result if I specify sizeof(char) * 56. Why 56? What's that value supposed to mean? I do realise that I have 6 key: value pairs and that 6 * 8 + 8 is 56, but I still don't get it.
[EDIT]
The use case is this:
The program connects to a server based on connection parameters (IP address, port) that are provided in a yaml configuration file. A minimalistic version of that file could look like this:
---
# Connection parameters
tx_addr: 192.168.1.124
tx_port: 8080
The program needs the values of the key:value pairs as those are passed to functions which establish the connection to the server. So, what I need to do is to parse the configuration file and look for allowed keys (the parameters could be anything; I only know the keys). E.g., if the key "tx_addr" is found, the program knows that the value to that key shall be passed as the IP address parameter of the functions establishing the server connection.
I assume that your aim is to copy all key:value pairs from tmp to params.
You can't copy all the key:value pairs from tmp to params with a single memcpy. You can't be sure that malloc have given you two consecutive memory areas.
You have to copy the strings one-by-one.
parseFile(tmp, argv[1]);
for (i=0; i<PARAMS; i++) {
memcpy(params[i], tmp[i], 32);
}
In order to get two consecutive memory areas (so that you can copy with a single memcpy), you need to allocate the variables as 2D arrays (https://stackoverflow.com/a/40847465/4386427 and https://stackoverflow.com/a/42094467/4386427).

Create a 2D Dynamic String Array [duplicate]

This question already has answers here:
C dynamically growing array
(10 answers)
Closed 6 years ago.
Can anyone explain to me the easiest way to create dynamically a 2D string array with stable second dimension? I've got a txt file with some strings in it and I want to transfer this txt to an array. So I want to associate the txt line number with the first dimension and the string itself with the second dimension. The second dimension is the number of characters in every row (which is stable because every line in the txt has a certain syntax) So if I have in my txt:
hello how (newline)
are youuu
*(I wrote youuu because as I said, every line has the same number of characters).
I want something like:
array[0]["hello how"],
array[1]["are youuu"]
Non numerical keys are not allowed in C. You're trying to do some PHP and JavaScript nonsense in a language that only works with numbers.
But, with C there is always 2 roads to hell.
char *lookup_key(int index, char *key) { ... }
printf(lookup_key(0, "hello how"));
If you know the length of the strings and how many you have, you can configure the array like this
char strings[numLines][strLen+1];
you can then access the array like this
strcpy(strings[1], "test2");
If you don't know anything beforehand, you need a pointer to pointer array and then use malloc to allocate space as the array grow, free when you are done.
dynamic in C implies you will need to use one of [c][m]alloc to create memory for your strings. And 2D implies an array of char arrays. Assuming you know the number of strings and the longest string needed, the following will create memory to contain them:
char ** Create2DStr(ssize_t numStrings, ssize_t maxStrLen)
{
int i;
char **a = {0};
a = calloc(numStrings, sizeof(char *));
for(i=0;i<numStrings; i++)
{
a[i] = calloc(maxStrLen + 1, 1);
}
return a;
}
The following will free the memory created above:
void free2DStr(char ** a, ssize_t numStrings)
{
int i;
for(i=0;i<numStrings; i++)
{
if(a[i]) free(a[i]);
}
free(a);
}
These can be called like this:
...
char **strArray = {0};
strArray = Create2DStr(10, 20);
//Use strArray...
free2DStr(10);
Giving 10 arrays, each able to contain 20 char, plus a NULL. (The + 1 after maxStrLen provides the extra space for the NULL).
If you want to save each line of the file as a row in the array, use a 2D array of char:
char fileContents[NUM_LINES][LINE_LENGTH + 1]; // +1 for zero terminator
If you don't know how many lines you have up front, you'll need to do some memory management. First, you'll need to allocate an initial extent:
#define INITIAL_EXTENT 20 // or some good starting point
char (*fileContents)[LINE_LENGTH + 1] = malloc( sizeof *fileContents * INITIAL_EXTENT );
if ( !fileContents )
{
// malloc failed; fatal error
fprintf( stderr, "FATAL: could not allocate memory for array\n" );
exit( EXIT_FAILURE );
}
size_t numRows = INITIAL_EXTENT; // number of rows in array
size_t rowsRead = 0; // number of rows containing data
As you read from the file, you'll check to make sure you have room in the array; if you don't, you'll need to extend the array with a realloc call, which is a potentially expensive operation. A common technique is to double the size of the array each time you extend it - that minimizes the total number of realloc calls. The risk is some internal fragmentation if you double the array size because you need just one more row, but that's probably something you can analyze around:
char tmpBuf[LINE_LENGTH + 2]; // account for newline in input buffer
while ( fgets( tmpBuf, sizeof tmpBuf, inputFile ) )
{
/**
* Check to see if you have any room left in your array; if not,
* you'll need to extend it. You'll probably want to factor this
* into its own function.
*/
if ( rowsRead == numRows )
{
/**
* Use a temporary variable for the result of realloc in case of failure
*/
char (*tmp)[LINE_LENGTH + 1] =
realloc( fileContents, sizeof *fileContents * ( 2 * numRows ) );
if ( !tmp )
{
/**
* realloc failed - we couldn't extend the array any more.
* Break out of the loop.
*/
fprintf( stderr, "ERROR: could not extend fileContents array - breaking out of loop\n" );
break;
}
/**
* Otherwise, set fileContents to point to the new, extended buffer
* and update the number of rows.
*/
fileContents = tmp;
numRows *= 2;
}
// strip the newline from the input buffer
char *newline = strchr( tmpBuf, '\n' );
if ( newline )
*newline = 0;
strcpy( fileContents[rowsRead++], tmpBuf );
}

Passing a non-empty string to snprintf causes an unrelated char* array to change addresses

I'm working on the exercises in K&R's book, and I've run into a weird bug while trying to extend 04-06 to allow for variables with string names. Truthfully, I've actually managed to fix the bug (pretty simple - explained below), but I'd like to know why the error was occuring in the first place.
For those unfamiliar with the problem, you're basically asked to create a command-line calculator (using Polish notation) that can store and recall variables with character names.
Here's the relevant code where the issue occurs:
#define MAXOPLEN 1000
int varCount = 1;
char **keys;
char **values;
// changing the declaration to:
// char strOps[][STROPCOUNT] = { ... };
// fixed the issue
char *strOps[STROPCOUNT] = { "dupe", "swap", "del", "print",
"clr", "sin", "cos", "tan",
"exp", "pow", "ln", "log",
"mem", "re"};
main() {
keys = malloc(varCount * sizeof(char[MAXOPLEN]));
keys[0] = "ans";
values = malloc(varCount * sizeof(char[MAXOPLEN]));
values[0] = "0.0";
... // Other stuff related to the program
}
// flag is unrelated to the problem I'm asking about. It just checks to see
// if the variable name used to store value n is 'ans', which is where
// the last returned value is stored automatically
void memorize(char s[], double n, bool flag) {
... // small conditional block for flag
for (i = 0; i < varCount; i++) {
if (equals(keys[i], s)) {
found = True;
// Next line is where the program actually breaks
snprintf(values[i], MAXOPLEN, "%f", n);
break;
}
}
if (!found) {
i = varCount;
varCount++;
keys = realloc(keys, varCount * sizeof(char*));
keys[i] = malloc(sizeof(char[MAXOPLEN]));
keys[i] = s;
values = realloc(values, varCount * sizeof(char*));
values[i] = malloc(sizeof(char[MAXOPLEN]));
snprintf(values[i], MAXOPLEN, "%f", n);
}
}
After compiling and running, the first time you enter in an equation to calculate, everything seems to run smoothly. However, while debugging, I found out that the first three char* in strOps were oddly made to point to different addresses. When trying to save the return value of the equation to "ans", it enters the for-loop in memorize() that tries to see if string s had been used as a key name already. It correctly finds keys[0] to point to a string matching s's value ("ans"), then attempts to convert double n to a string and save it in values[0].
While inside the snprintf() function, the first three char* in strOps are made to point elsewhere inside this method in corecrt_stdio_config.h:
_Check_return_ _Ret_notnull_
__declspec(noinline) __inline unsigned __int64* __CRTDECL __local_stdio_printf_options(void)
{
// Error occurs after this next line:
static unsigned __int64 _OptionsStorage;
return &_OptionsStorage;
}
As commented in the code above, making strOps a 2D array of characters (rather than an array of char pointers) fixed the issue. This makes sense because arrays of characters can't have the values of individual characters changed, but what I don't understand is why the that method in corecrt_stdio_config.h was changing the values of those three pointers in the first place.
Thanks!
Your initializations are incorrect and are causing the change:
keys[0] = "ans";
values[0] = "0.0";
Both "ans" and "0.0" are string literals and cannot be used to initialize the arrays, you need to use strcpy after you allocate.
strcpy (keys, "ans");
strcpy (values, "0.0");
Your other option is to assign one character at a time:
size_t i;
char *p = "ans";
for (i = 0; i < strlen (p); i++)
keys[i] = p[i]; /* copy to keys */
p[i] = 0; /* nul-terminate */
note: these are examples of your errors, you do the same thing throughout your code.

vector replication in c

how can i define an array in c which works like vector? This array should take any amount of values. It can take 0 values or 10 values or 100 values.
The code below works but gives me a runtime error that stack was corrupted.
int i = 0;
int* aPtr = &i;
int* head = aPtr;
for(i=0;i<6;i++){
(*aPtr)=i;
aPtr++;
}
Similarly how can i use char* str to take any amount of characters followed by null character in end to make a string?
Practice for interviews :)
There are many ways to do this in C, depending on your requirements, but you said "any number of values" (which usually means as many as will fit in memory). That's commonly done using realloc to grow the size of an array dynamically. You'll need to keep some bookkeeping information too on the size of the array as it grows.
void
store (vector_t * v, int idx, int value)
{
if (v->size < idx) {
v->size = idx * 2;
v->data = realloc(v->data, v->size);
}
v->data[idx] = value;
}
This being tagged "homework", I've left some details to fill in such as the definition of vector_t.
In Your for loop , after the first iteration, you are trying to access aPtr which points to a memory location which was not declared or reserved before. In the first iteration, the int i did the memory allocation for you.
What you could do though would be to initally allocate the memory required using malloc .
Once this memory is allocated , and if you walk through only the allocated stack space, you wont come across a run time error.
PS:Your code does not work if it just compiles. Any program may contain run time as well as compile time errors. Your code sample is a very common example of run-time error.
This isn't too difficult. The important thing to remember is that you will need to initially allocate memory for your array using malloc(...) or calloc(...). After that you can easily allocate (or deallocate) memory as items are added or removed. The method for dynamically adding or removing memory (which is used to store the items in the array) is realloc(...). The wiki page for C Dynamic Memory Allocation is actually pretty informative. I've provided an example below showing how to initially allocate a char* array, then increase the size and decrease the size.
#include "stdio.h"
#include "stdlib.h"
int main()
{
char *myDynamicString;
/* allocate initial memory */
myDynamicString = (char *)malloc(sizeof(char) * 2);
myDynamicString[1] = '\0';
/* set values */
myDynamicString[0] = 'A';
/* prints: A */
printf("String: %s\n", myDynamicString);
/* make string bigger */
myDynamicString = (char *)realloc(myDynamicString, sizeof(char) * 6);
myDynamicString[5] = '\0';
/* set values */
myDynamicString[1] = 'P';
myDynamicString[2] = 'P';
myDynamicString[3] = 'L';
myDynamicString[4] = 'E';
/* prints: APPLE */
printf("Bigger String: %s\n", myDynamicString);
/* make string smaller */
myDynamicString = (char *)realloc(myDynamicString, sizeof(char) * 3);
myDynamicString[2] = '\0';
/* set values */
myDynamicString[1] = 'Z';
/* prints: AZ */
printf("Smaller String: %s\n", myDynamicString);
/* don't forget to release the memory */
free(myDynamicString);
return 0;
}

C Program: newbie confusion on working with character string arrays

I'm getting confused working with character string arrays. I'm trying to fill 2 arrays in a for loop. Within each array, all elements are the same.
To conserve memory, for array_person_name I attempt to simply copy the pointer to the string stored by person_name. For array_param, the string that it stores the pointer to is always 2 characters long (e.g. "bt") plus the null termination character , and here I also attempt to conserve memory by storing the pointer to "bt" in array_param.
Since the number of array elements, arraysize, is downloaded from a database when the program runs, I use malloc to allocate memory. Since my OS is 64 bit (Linux x86-64), I allocate 8 bytes for each of arraysize pointers. Although not shown, I free these two arrays at the end of the program.
int kk, arraysize;
char person_name[101] = "";
char * array_person_name;
char * array_param;
...
strncpy(person_name, "John Smith", 100);
arraysize = <this value is downloaded from database>;
...
array_person_name = malloc( 8 * arraysize ); /* 8 is for 64b OS */
array_param = malloc( 8 * arraysize );
for (kk = 0; kk < arraysize; kk++) {
array_person_name[kk] = &person_name;
array_param[kk] = &"bt";
}
/* test results by printing to screen */
printf("Here is array_person_name[0]: %s\n", array_person_name[0]);
printf("here is array_param[0]: %s\n", array_param[0]);
The compiler returns the warnings: warning: assignment makes integer from pointer without a cast on the two lines inside the for loop. Any idea what I'm doing wrong?
Since you want each item in array_person_name and array_param to be a pointer to person_name/"bt", you want a char **:
char **array_person_name;
array_person_name = malloc(arraysize * sizeof(*array_person_name));
for (int i=0; i<arraysize; i++)
array_person_name[i] = person_name;
You're assigning a pointer to array person_name to character defined by array_person_name[kk]. What you probably meant to do was to define array_person_name as a char** type.
You shouldn't be assuming 8 bytes because it's 64 bit. You should leave that part to C and use sizeof() operator.

Resources