a malloc question.
First, I generate some strings.
And second, I allocate none-space for copying pointers which point to those strings.
At this moment the program should probably crash, since I ve tried to copy those pointers to nowhere, but it doesn’t crash. How is it possible?
Any comments would be appreciated.
#include <stdio.h>
int main(int argc, char *argv[])
{
int i,
iJ,
iCharCount = 0,
iCharVal,
iStrLen = 0,
iStrNum = 50,
iStrCount = 0,
iAllocSize = 0;
char *pcStr,
*pcStr_CurPos,
**ppcStr,
**ppcStr_CurPos;
// suppose, an average length of string is * N bytes.
iAllocSize = iStrNum * 6;
iStrCount = 0;
// allocate ...
pcStr = pcStr_CurPos = (char*) malloc (iAllocSize);
if (pcStr==NULL){printf("NULL == malloc()\n"); exit (1);}
for (i=0; i < iStrNum; i++)
{
iStrCount++;
iStrLen = rand() % 7 + 2; // is in the range 2 to 8
printf("Len of Str=%d; str=[", iStrLen);
for (iJ = 0; iJ < iStrLen-1; iJ++)
{
// A-Z a-z
iCharVal = rand() % 58 + 65;
if (iCharVal > 90 && iCharVal < 97) {iJ--; continue;}
if (pcStr_CurPos < pcStr + iAllocSize )
{
printf ("%c", iCharVal);
*pcStr_CurPos++ = iCharVal;
iCharCount ++;
}
else
{
*pcStr_CurPos++ = 0;
iCharCount ++;
printf ("]\n");
goto exit;
}
}
printf ("]\n");
*pcStr_CurPos++ = 0;
iCharCount ++;
}
exit:
// I allocate NOTHING, ...
ppcStr = ppcStr_CurPos = (char**) malloc (0); // ZERO !
// Copying pointers ...
pcStr_CurPos = pcStr;
while(pcStr_CurPos < pcStr + iCharCount)
{
//... BUT IT WORKS AND DON'T CRASH.
// HOW IS IT POSSIBLE ???
*ppcStr_CurPos++ = pcStr_CurPos;
while (*pcStr_CurPos++) ;
}
ppcStr_CurPos = ppcStr;
iStrNum = iStrCount;
printf ("\n Output ppcStr:\n", iCharCount );
while(iStrNum--)
{
printf("[%d][%s]\n", iStrNum, *(ppcStr_CurPos++));
}
printf ("Press Enter key or sth\n");
getchar();
return 0;
}
In general, C is not guaranteed to crash if you access uninitialized memory. The behavior is undefined, which means anything could happen. The compiler could make demons fly out of your nose, as they say.
I believe that in practice the system may allocate more memory than you requested. So here
ppcStr = ppcStr_CurPos = (char**) malloc (0); // ZERO !
the actual size of the block may be 1 or more:
If the size of the space requested is zero, the behavior is implementation-defined: either a null pointer is returned, or the behavior is as if the size were some nonzero value, except that the returned pointer shall not be used to access an object.
Thus if the returned pointer is not null, it may point to a buffer of nonzero length.
Then here
pcStr_CurPos = pcStr;
while(pcStr_CurPos < pcStr + iCharCount)
{
//... BUT IT WORKS AND DON'T CRASH.
// HOW IS IT POSSIBLE ???
*ppcStr_CurPos++ = pcStr_CurPos;
while (*pcStr_CurPos++) ;
}
after copying the 1st byte into the "0 byte" buffer, in the inner while you seem to be stepping pcStr_CurPos through till the end of the string it points to, then consequently exit from the outer loop. Thus no more bytes are copied!
So I would venture to say that in practice you didn't necessarily write to unallocated memory (although according to the above quote it is still undefined behaviour to write to that buffer - but as #novalis rightly pointed out, undefined behaviour may not necessarily result in a crash).
Related
This code compiles successfully but when I debug it shows a SIGSEV seg fault error. Can someone help please?
char *_strdup(char *str)
{
int i, size = 0;
char *mp;
if (str == NULL)
{
return (NULL);
}
for (; str[size] != '0'; size++)
mp = malloc(size * sizeof(str) + 1);
/* + 1 to get last part of the str */
if (mp == 0)
{
return (NULL);
}
else
{
for (; i < size; i++)
{
mp[i] = str[i];
}
}
return (mp);
}
First, just because it compiles successfully, this does not mean that your code is correct. It just means that syntactically the compiler is fine. I hope you use the maximum warning level and correct your code until all warnings and errors are gone.
You have multiple problems:
You seem to look for the terminating end-of-string marker. But instead of the correct '\0' you typed '0'. This can lead to a much too big size, depending where a zero digit is found. Depending on your system, a segmentation fault is also possible.
sizeof is an operator that yields the size of its argument, in your case the size of a pointer. str is of type char *. Effectively you allocate too much, but this is harmless.
The for loop uses the memory allocation as its body. I'm sure you didn't mean this, but there is no empty statement. So you are allocating multiple memory spaces, which are leaks in the end.
An empty statement is a single semicolon or an empty pair of curly braces.
What you most probably want to achieve is to find the number of characters that str points to. You can get it by calling strlen(str).
i is not initialized, it can have any value. This can lead to a segmentation fault, if it starts with a negative value.
You did not add the end-of-string marker in the duplicate. Depending on the other code we don't see, this can lead to segmentation faults.
This is a possible solution without calling strlen():
char *_strdup(const char *str)
{
int i;
int size;
char *mp;
if (str == NULL)
{
return NULL;
}
for (size = 0; str[size] != '\0'; size++)
{
/* just looking for the end of the string */
}
size++;
/* + 1 for the end-of-string marker */
mp = malloc(size);
if (mp == NULL)
{
return NULL;
}
for (i = 0; i < size; i++)
{
mp[i] = str[i];
}
return mp;
}
I made a bit more:
Use separate variable definitions, it avoid errors and eases maintenance.
return is not a function and needs no parentheses for its expression.
Put the initialization of the index variable where it belongs, in the initializing statement of for. This way everything about this index is at one place.
Consider the end-of-string marker by incrementing size. This eases the following code.
Since sizeof (char) is 1, it can be ommitted at the calculation of the needed memory size.
Compare mp with NULL instead of 0. It is a pointer, and this is C, not C++.
Your variable i has been declared but not initialized so a random number is used in your for(; i < size;
Just add int i = 0, size = 0; at the beginning or change your for statement to for(i = 0; i < size; i++)
This was the reason for your segmentation fault. Some other issues:
As mentioned in comments string termination character is not '0'. It's either 0 or '\0'.
You are calling malloc on each iteration of your for statement. This causes memory leak.Just call it once after you got your string size right. This is fixed by putting a semicolon after the for.
Maybe something like this.
char *_strdup (char *str)
{
int i, size;
char *mp;
if (str == NULL)
{
return (NULL);
}
for (size = 0; str[size] != 0; size++);
mp = malloc (size * sizeof (str) + 1);
/* + 1 to get last part of the str */
if (mp == 0)
{
return (NULL);
}
else
{
for (i = 0; i < size; i++)
{
mp[i] = str[i];
}
}
return (mp);
}
Could you help please ?
When I execute this code I receive that:
AAAAABBBBBCCCCCBBBBBCOMP¬ıd┐╔ LENGTH 31
There are some weirds characters after letters, while I've allocate just 21 bytes.
#include <stdio.h>
#include <stdlib.h>
char * lineDown(){
unsigned short state[4] = {0,1,2,1};
char decorationUp[3][5] = {
{"AAAAA"},{"BBBBB"},{"CCCCC"}
};
char * deco = malloc(21);
int k;
int p = 0;
for(int j = 0; j < 4; j++){
k = state[j];
for(int i = 0; i < 5; i++){
*(deco+p) = decorationUp[k][i];
p++;
}
}
return deco;
}
int main(void){
char * lineDOWN = lineDown();
int k = 0;
char c;
do{
c = *(lineDOWN+k);
printf("%c",*(lineDOWN+k));
k++;
}while(c != '\0');
printf("LENGTH %d\n\n",k);
}
The function does not build a string because the result array does not contain the terminating zero though a space for it was reserved when the array was allocated.
char * deco = malloc(21);
So you need to append the array with the terminating zero before exiting the function
//...
*(deco + p ) = '\0';
return deco;
}
Otherwise this do-while loop
do{
c = *(lineDOWN+k);
printf("%c",*(lineDOWN+k));
k++;
}while(c != '\0')
will have undefined behavior.
But even if you will append the array with the terminating zero the loop will count the length of the stored string incorrectly because it will increase the variable k even when the current character is the terminating zero.
Instead you should use a while loop. In this case the declaration of the variable c will be redundant. The loop can look like
while ( *( lineDOWN + k ) )
{
printf("%c",*(lineDOWN+k));
k++;
}
In this case this call
printf("\nLENGTH %d\n\n",k);
^^
will output the correct length of the string equal to 20.
And you should free the allocated memory before exiting the program
free( lineDOWN );
As some other wrote here in their answers that the array decorationUp must be declared like
char decorationUp[3][6] = {
{"AAAAA"},{"BBBBB"},{"CCCCC"}
};
then it is not necessary if you are not going to use elements of the array as strings and you are not using them as strings in your program.
Take into account that your program is full of magic numbers. Such a program is usually error-prone. Instead you should use named constants.
In
char decorationUp[3][5] = {
{"AAAAA"},{"BBBBB"},{"CCCCC"}
};
your string needs 6 characters to also place the null char, even in that case you do not use them as 'standard' string but only array of char. To get into the habit always reverse the place for the ending null character
you can do
char decorationUp[3][6] = {
{"AAAAA"},{"BBBBB"},{"CCCCC"}
};
Note it is useless to give the first size, the compiler counts for you
Because in main you stop when you read the null character you also need to place it in deco at the end, so you need to allocate 21 for it. As before you missed the place for the null character, but here that produces an undefined behavior because you read after the allocated block.
To do *(deco+p) is not readable, do deco[p]
So for instance :
char * lineDown(){
unsigned short state[] = {0,1,2,1};
char decorationUp[][6] = {
{"AAAAA"},{"BBBBB"},{"CCCCC"}
};
char * deco = malloc(4*5 + 1); /* a formula to explain why 21 is better than 21 directly */
int k;
int p = 0;
for(int j = 0; j < 4; j++){
k = state[j];
for(int i = 0; i < 5; i++){
deco[p] = decorationUp[k][i];
p++;
}
}
deco[p] = 0;
return deco;
}
I am experiencing an issue where the invocation of realloc seems to modify the contents of another string, keyfile.
It's supposed to run through a null-terminated char* (keyfile), which contains just above 500 characters. The problem, however, is that the reallocation I perform in the while-loop seems to modify the contents of the keyfile.
I tried removing the dynamic reallocation with realloc and instead initialize the pointers in the for-loop with a size of 200*sizeof(int) instead. The problem remains, the keyfile string is modified during the (re)allocation of memory, and I have no idea why. I have confirmed this by printing the keyfile-string before and after both the malloc and realloc statements.
Note: The keyfile only contains the characters a-z, no digits, spaces, linebreaks or uppercase. Only a text of 26, lowercase letters.
int **getCharMap(const char *keyfile) {
char *alphabet = "abcdefghijklmnopqrstuvwxyz";
int **charmap = malloc(26*sizeof(int));
for (int i = 0; i < 26; i++) {
charmap[(int) alphabet[i]] = malloc(sizeof(int));
charmap[(int) alphabet[i]][0] = 0; // place a counter at index 0
}
int letter;
int count = 0;
unsigned char c = keyfile[count];
while (c != '\0') {
int arr_count = charmap[c][0];
arr_count++;
charmap[c] = realloc(charmap[c], (arr_count+1)*sizeof(int));
charmap[c][0] = arr_count;
charmap[c][arr_count] = count;
c = keyfile[++count];
}
// Just inspecting the results for debugging
printf("\nCHARMAP\n");
for (int i = 0; i < 26; i++) {
letter = (int) alphabet[i];
printf("%c: ", (char) letter);
int count = charmap[letter][0];
printf("%d", charmap[letter][0]);
if (count > 0) {
for (int j = 1; j < count+1; j++) {
printf(",%d", charmap[letter][j]);
}
}
printf("\n");
}
exit(0);
return charmap;
}
charmap[(int) alphabet[i]] = malloc(sizeof(int));
charmap[(int) alphabet[i]][0] = 0; // place a counter at index 0
You are writing beyond the end of your charmap array. So, you are invoking undefined behaviour and it's not surprising that you are seeing weird effects.
You are using the character codes as an index into the array, but they do not start at 0! They start at whatever the ASCII code for a is.
You should use alphabet[i] - 'a' as your array index.
The following piece of code is a source of troubles:
int **charmap = malloc(26*sizeof(int));
for (int i = 0; i < 26; i++)
charmap[...] = ...;
If sizeof(int) < sizeof(int*), then it will be performing illegal memory access operations.
For example, on 64-bit platforms, the case is usually sizeof(int) == 4 < 8 == sizeof(int*).
Under that scenario, by writing into charmap[13...25], you will be accessing unallocated memory.
Change this:
int **charmap = malloc(26*sizeof(int));
To this:
int **charmap = malloc(26*sizeof(int*));
I'm not sure why malloc is allocating so much space. Here's a snippet of the problem code:
char * hamming_string = NULL;
void enter_params(){
printf("Enter the max length: ");
scanf_s("%d", &max_length);
hamming_string = (char *) malloc(max_length * sizeof(char));
// to test what's going on with the hamming string
for(int i = 0; i < strlen(hamming_string); i++){
hamming_string[i] = 'a';
}
printf("hamming string = %s", hamming_string);
}
I set max_length to 2 and I'm seeing 12 a's. In another function, I was going to have the user input the hamming string using scanf_s("%s", &hamming_string); but I kept getting a access violation
hamming_string is not a string until one of its elements is a '\0'.
The str*() functions can only be used on strings.
Your program invokes Undefined Behaviour (by calling strlen() with something that is not a string).
malloc() allocates the amount of space that you ask for but it does not initialise it. When you call strlen() it scans the memory starting at what hamming_string points to and continues until it finds a null or it accesses memeory that it shouldn't and causes an exception.
In addition you need to allocate space for the null at the end of the string, if you want a string to hold 2 characters you need to allocate 3 characters to allow for the terminating null.
You are asking for the strlen of an uninitialized variable (this is undefined behaviour):
strlen(hamming_string);
(m)allocate one more in order to store the trailling \0:
hamming_string = malloc(max_length + 1);
change to
for(int i = 0; i < max_length; i++){
hamming_string[i] = 'a';
}
and don't forget to add the trailling \0 after the for loop:
hamming_string[i] = '\0'; /* or use calloc and skip this line */
void check_code(){
int actual_length, parity_bit, error_bit = 0, c = 0, i, j, k;
printf("Enter the Hamming code: ");
scanf_s("%s", &hamming_string);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This scanf_s() call is incorrect.
According to the C11 documentation or MSDN documentation it needs to be
scanf_s("%s", hamming_string, size - 1);
Note that you don't know size inside the function.
Note that you don't pass the address of hamming_string; hamming_string by itself gets converted to the address of its first element.
Example1:
char *hamming_string = malloc((max_length + 1) * sizeof(char));
for (i = 0; i < max_length; i++)
{
hamming_string[i] = 'a';
}
hamming_string[i] = '\0';
printf("hamming string = [%s]\n", hamming_string);
Output:
sdlcb#Goofy-Gen:~/AMD$ ./a.out
hamming string = [aaaaaaaaaaaa]
Example2:
char s;
for (i = 0; i < max_length; i++)
{
scanf(" %c", &s);
hamming_string[i] = s;
}
hamming_string[i] = '\0';
printf("hamming string = [%s]\n", hamming_string);
Output:
sdlcb#Goofy-Gen:~/AMD$ ./a.out
a
b
c
d
e
f
g
h
i
j
k
l
hamming string = [abcdefghijkl]
Basically, I'm trying to convert a bunch of char inputs to ints and assign them to a dynamic int array. The string input and tokenization seem to work fine. The issue (from what I can tell) seems to be with the reallocation of the int array; after the array is reallocated twice, the pointer to the int array returns NULL.
What I tried to do was double the size of the int array every time the number of tokens meets or surpasses (size divided by sizeof(int)). The realloc statement works each time this condition is met.
I thought using a pointer to a pointer was the end-all solution to this. I bet it's some really obvious issue, but I'm at my wit's end here. If you request any further elaboration, I'll try my best. Understand that I've only taken C for a semester and have struggled most of the way.
Also, truth be told, this was part of a class assignment which has since passed. I'd prefer an explanation about what's wrong more than a full-on code, if that's alright.
I have a lot of printf statements, so apologies for any clutter.
EDIT: Replaced all instances of newArray within the input() function with *resize. However, I've never tried assigning values through pointers to pointers, so feel free to correct me with a syntactic example if you know how I messed up. Segmentation fault occurs here:
for (k = (numElem - count); k < numElem; k++)
{
printf("\nk = %i\n", k);
printf("j = %i\n", j);
printf("numElem = %i\n", numElem);
printf("results[j]: %s\n\n\n", results[j]);
/* Segmentation fault regardless of what is assigned
to *resize[k]. */
*resize[k] = atoi(results[j]); // PROBLEM HERE
j++;
}
The source code has been updated to reflect upon this. To make this ridiculously long post a little more subdued, let's state that I did this in main():
int *newArray = malloc(MAXTOKEN * sizeof(int));
input(&newArray);
free(newArray);
Moving on.
/* String input takes in char values,
tokenizes them, converts the results
to int, assigns them to newresizeay. */
int input(int **resize)
{
int i, j, k, count;
int numElem = 0;
int currentSize = MAXTOKEN;
char str[MAXSTRING];
char *results[MAXTOKEN];
/* This entire loop takes place at least once,
provided the first input isn't NULL. */
do
{
i = 0, j = 0, k = 0;
/* Char input process. Takes place until the user
presses ENTER. */
printf("Input integer values separated by spaces, or "
"press ENTER to exit.\n");
while ( ((str[i] = getchar() ) != '\n') && (i < MAXSTRING) )
i++;
printf("\n\n");
str[i] = '\0';
/* Tokenization of the chars that were input */
count = 0;
if (results[0] = strtok(str, " \t"))
count++;
while (results[count] = strtok(NULL, " \t") )
count++;
/* numElem = 1 if the first input prompt established
str[0] as NULL */
if ( (count < 1) && (numElem < 1) )
count = 1;
numElem += count;
printf("numElem: %i\ncurrentSize: %i\n", numElem, currentSize);
/* If the number of elements to assign meet or surpass
the amount of [memory / sizeof(int)], exponentially
increase the size of the int resizeay. */
if ( numElem >= currentSize )
{
*resize = realloc(*resize, (currentSize) * sizeof(int));
if (*resize == NULL)
printf("\n\nYep, it threw up.\n\n");
currentSize *= 2;
}
printf("\nSize should be: %i\n", currentSize * 4);
printf("Actual size: %d\n", _msize(*resize));
/* The tokenized chars are converted to integers and
assigned to the int resizeay. */
for (k = (numElem - count); k < numElem; k++)
{
printf("\nk = %i\n", k);
printf("j = %i\n", j);
printf("numElem = %i\n", numElem);
printf("results[j]: %s\n\n\n", results[j]);
*resize[k] = atoi(results[j]); // PROBLEM HERE
j++;
}
for (i = 0; i < numElem; i++)
printf("resize[%i]: %i\n", i, *resize[i]);
printf("\n\n\n");
} while (str[0] != NULL);
}
The input function receives both resize and arr. main sends the same pointer to both. This is a bug.
When resize is resized, arr stays the same and may point to an invalid address (when realloc returns a different address).
How to fix:
Remove arr function argument and only use resize.
When you call the realloc function,if the new memory block is smaller than previous ,it will maintain the original state pointing to the memory block which previous used.If the new memory block is larger than previous,the system will re allocate memory on the heap and the previous memory is released.
Among other problems:
char *results[MAXTOKEN];
should be
char *results[MAXTOKEN + 1];
because here the maximum value of count will be MAXTOKEN in this loop :
while (results[count] = strtok(NULL, " \t") )
count++;
and
char str[MAXSTRING];
is pretty scary, because as soon as the user enters more than MAXSTRIN (=11) characters without pressing Enter, you will get a buffer overflow.