I create a buffer like this but I don't give it content. Then I try to view strlen() of this block memory.
int size = 24;
char *c;
c = (char *)malloc(size);
printf("%d\n", strlen(c));
What I get is not 24 but 40. I try to view the value from c[40] to c[47] and I always get \0 but after c[47] is not null anymore.
When I set size = 18, the result isn't 40 anymore. It's 32. And values from c[32] to c[47] are all \0.
When I set size = 7, the result is 24 and values form c[24] to c[47] are all \0.
I know using strlen for an array like this is not able to give me the size I used in malloc() function.
I just wonder why this happened and when we change the value of size, how the result change? Is there anything we can deal with using this?
Edit: It seem like everyone think the result is unpredictable. It's the fact that it's always a multiple of 8 and when we increase the size, there is a limit where the result increase. We can determine exactly the value of size that make the result change and it doesn't change despite how many times we test. Does it depend on OS not just C language or compiler? Why 8 is chosen?
The memory you allocated isn't initialized. Passing it to strlen, a function that expects a NUL terminated string, has undefined behavior. So you can get whatever, you don't even have to get any result.
There is not built-in way in C to know the exact size of an allocated block of memory.
You should read the documentation, strlen() is for the length of strings. Without much detail I will tell you one thing,
There is no way to get the length of a pointer dynamically, so your only option is to store it and use it later.
A simple elegant method, is to use a struct for that, where you store the size and use it every time you need.
struct pointer_type {
void *pointer;
size_t allocated_size;
};
As for the "What i get is not 24 but 40" question,
Your pointer, is uninitialized. This causes what is known as undefined behavior because the way strlen() works is by dereferencing the pointer and counting characters until a given character occurs, something like
int strlen(const char *const str)
{
int count = 0;
while (*(str++) != '\0') ++count;
return count;
}
and since your pointer points to random garbage, this will not work right and the returned value is unpredictable.
Related
Here's what I did:
#include <stdio.h>
#include <string.h>
int main() {
char name[] = "longname";
printf("Name = %s \n",name);
strcpy(name,"evenlongername");
printf("Name = %s \n",name);
printf("size of the array is : %d",sizeof(name));
return 0;
}
It works, but how? I thought that once memory is assigned to an array in a program, it is not possible to change it. But, the output of this program is:
Name = longname
Name = evenlongername
size of the array is 9
So the compiler affirms that the size of the array is still 9. How is it able to store the word 'evenlongername' which has a size of 15 bytes (including the string terminator)?
In this case, name is allocated to fit "longname", which is 9 bytes. When you copy "evenlongername" into it, you're writing outside of bounds of that array. It's undefined behavior to write outside of the bounds, this means it may or may not work. Some times, it'll work, other times you'll get seg fault, yet other times you'll get weird behavior.
So the compiler affirms that the size of the array is still 9. How is it able to store the word 'evenlongername' which has a size of 15 bytes(including the string terminator)?
You are using a dangerous function (see Bugs), strcpy, which blindly copies source string to destination buffer without knowing about its size; in your case of copying 15 bytes into a buffer with size 9 bytes, essentially you have overflown. Your program may work fine if the memory access is valid and it doesn't overwrite something important.
Because C is a lower-level programming language, a C char[] is "barebone" mapping of memory, and not a "smart" container like C++ std::vector which automatically manages its size for you as you dynamically add and remove elements. If you are still not clear about the philosophy of C in this, I'd recommend you read *YOU* are full of bullshit. Very classic and rewarding.
Using sizeof on a char array will return the size of the buffer, not the length of the null-terminated string in the buffer. If you use strcpy to try and overflow the array, and it just happens to work (it's still undefined behavior), sizeof is still going to report the size used at declaration. That never changes.
If what you're interested in is observing how the length of a string changes with different assignments:
Use an adequate buffer to store every string you're going to test.
Use the function strlen in <string.h> which will give you the actual length of the string, and not the length of your buffer, which, once declared, is constant.
code:
sprintf(tmp, "xbitmap_width %d\n", symbol->scale);
Output:
xbitmap_width 1075052544
expected output - value of scale which is 5 so it should be:
xbitmap_width 5
What am i missing??? Why is sprintf taking pointer value?
Update:
If symbol->scale is indeed not a pointer, then also ensure tmp is big enough, to avoid overflow. I hope tmp is at least 18 chars big, but best make it big enough (like 30 or bigger), and if it's allocated on the heap: initialize it to zeroes: memset or calloc(30, sizeof *tmp) would be preferable.
You may also want to ensure that symbol is not a stack value, returned by a function. This, too, would be undefined behaviour. However, given that you say you're using new or malloc (which _does not initialize the struct, BTW), that can't be the issue.
The not-initializing bit here (when using malloc) might be, though: malloc merely reserves enough memory to store a given object one or more times. The memory is not initialized, though:
char *str = malloc(100);
Is something like that thing where you give a bunch of monkeys type-writers: eventually one of them might wind up punching in a line of Shakespeare: well, if you malloc strings like this, and print them, eventually one of them might end up containing the string "Don't panic".
Now, this isn't exactly true, but you get the point...
To ensure your struct is initialized, either use calloc or memset those members that str giving you grief.
if your struct looks like this:
struct symbol
{
int *scale;
}
Then you are passing the value of scale to sprintf. This value is a memory address, not an int. An int, as you may no is guaranteed to be at least 2 bytes in size (most commonly it's 4 though). A pointer is 4 or 8 bytes in size, so passing a pointer, and have sprintf interpret it as an int, you get undefined behaviour.
To print 5 in your case:
struct symbol *symbol = malloc(sizeof *symbol);
int s = 5;
symbol->scale = &s;
printf("%d\n", *(symbol->scale));//dereference the scale pointer
But this is undefined behaviour:
printf("%d\n", symbol->scale);//passing pointer VALUE ==> memory address
//for completeness & good practices' sake:
free(symbol);
Oh, and as stated in the comments: snprintf is to sprintf what strncpy is to strcpy and strncat is to strcat: it's safer to use the function which allows you to specify a maximum of chars to set
#include<stdio.h>
#include<stdlib.h>
void main()
{
char *arr;
arr=(char *)malloc(sizeof (char)*4);
scanf("%s",arr);
printf("%s",arr);
}
In the above program, do I really need to allocate the arr?
It is giving me the result even without using the malloc.
My second doubt is ' I am expecting an error in 9th line because I think it must be
printf("%s",*arr);
or something.
do I really need to allocate the arr?
Yes, otherwise you're dereferencing an uninitialised pointer (i.e. writing to a random chunk of memory), which is undefined behaviour.
do I really need to allocate the arr?
You need to set arr to point to a block of memory you own, either by calling malloc or by setting it to point to another array. Otherwise it points to a random memory address that may or may not be accessible to you.
In C, casting the result of malloc is discouraged1; it's unnecessary, and in some cases can mask an error if you forget to include stdlib.h or otherwise don't have a prototype for malloc in scope.
I usually recommend malloc calls be written as
T *ptr = malloc(N * sizeof *ptr);
where T is whatever type you're using, and N is the number of elements of that type you want to allocate. sizeof *ptr is equivalent to sizeof (T), so if you ever change T, you won't need to duplicate that change in the malloc call itself. Just one less maintenance headache.
It is giving me the result even without using the malloc
Because you don't explicitly initialize it in the declaration, the initial value of arr is indeterminate2; it contains a random bit string that may or may not correspond to a valid, writable address. The behavior on attempting to read or write through an invalid pointer is undefined, meaning the compiler isn't obligated to warn you that you're doing something dangerous. On of the possible outcomes of undefined behavior is that your code appears to work as intended. In this case, it looks like you're accessing a random segment of memory that just happens to be writable and doesn't contain anything important.
My second doubt is ' I am expecting an error in 9th line because I think it must be printf("%s",*arr); or something.
The %s conversion specifier tells printf that the corresponding argument is of type char *, so printf("%s", arr); is correct. If you had used the %c conversion specifier, then yes, you would need to dereference arr with either the * operator or a subscript, such as printf("%c", *arr); or printf("%c", arr[i]);.
Also, unless your compiler documentation explicitly lists it as a valid signature, you should not define main as void main(); either use int main(void) or int main(int argc, char **argv) instead.
1. The cast is required in C++, since C++ doesn't allow you to assign void * values to other pointer types without an explicit cast
2. This is true for pointers declared at block scope. Pointers declared at file scope (outside of any function) or with the static keyword are implicitly initialized to NULL.
Personally, I think this a very bad example of allocating memory.
A char * will take up, in a modern OS/compiler, at least 4 bytes, and on a 64-bit machine, 8 bytes. So you use four bytes to store the location of the four bytes for your three-character string. Not only that, but malloc will have overheads, that add probably between 16 and 32 bytes to the actual allocated memory. So, we're using something like 20 to 40 bytes to store 4 bytes. That's a 5-10 times more than it actually needs.
The code also casts malloc, which is wrong in C.
And with only four bytes in the buffer, the chances of scanf overflowing is substantial.
Finally, there is no call to free to return the memory to the system.
It would be MUCH better to use:
int len;
char arr[5];
fgets(arr, sizeof(arr), stdin);
len = strlen(arr);
if (arr[len] == '\n') arr[len] = '\0';
This will not overflow the string, and only use 9 bytes of stackspace (not counting any padding...), rather than 4-8 bytes of stackspace and a good deal more on the heap. I added an extra character to the array, so that it allows for the newline. Also added code to remove the newline that fgets adds, as otherwise someone would complain about that, I'm sure.
In the above program, do I really need to allocate the arr?
You bet you do.
It is giving me the result even without using the malloc.
Sure, that's entirely possible... arr is a pointer. It points to a memory location. Before you do anything with it, it's uninitialized... so it's pointing to some random memory location. The key here is wherever it's pointing is a place your program is not guaranteed to own. That means you can just do the scanf() and at that random location that arr is pointing to the value will go, but another program can overwrite that data.
When you say malloc(X) you're telling the computer that you need X bytes of memory for your own usage that no one else can touch. Then when arr captures the data it will be there safely for your usage until you call free() (which you forgot to do in your program BTW)
This is a good example of why you should always initialize your pointers to NULL when you create them... it reminds you that you don't own what they're pointing at and you better point them to something valid before using them.
I am expecting an error in 9th line because I think it must be printf("%s",*arr)
Incorrect. scanf() wants an address, which is what arr is pointing to, that's why you don't need to do: scanf("%s", &arr). And printf's "%s" specificier wants a character array (a pointer to a string of characters) which again is what arr is, so no need to deference.
hey i am having problems using the sizeof operator in malloc. For example see the foll. code-
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
char * copy(char *s)
{
char *t=malloc(sizeof(s));
char *ptr=s;
int i=0;
do
{
t[i++]=*ptr++;
}
while(*ptr!='\0');
return t;
}
int main()
{
char *s="hello adsjahsjkdhjkashdkjaskdasldjlasjdlajsdlkjaslkdjalsjdlasjdljasdljasdkljklsdjlasdsadasdasd";
char *b=copy(s);
printf("%s\n",b);
free(b);
return 0;
}
on ideone, it gives the error:-
* glibc detected ./prog: free(): invalid next size (fast): 0x09bcf008 **
But when i replace malloc(sizeof(s)) with malloc(strlen(s)+1) , the program works perfectly. So whats the problem?
NOTE:this is just a small prog i created to demonstrate the problem i was having in another code.
The operator sizeof doesn't do what you want on pointers. It yields the size of the pointer on your machine (which will be something like 4 or 8).
You can think of it this way: the array decays to a pointer when passed to a function and the information regarding its size is "lost".
Also note your loop doesn't fill in the 0 terminator.
You should use strlen instead of sizeof in the copy function:
char * copy(char *s)
{
char *t=malloc(strlen(s) + 1);
char *ptr=s;
int i=0;
do
{
t[i++]=*ptr++;
}
while(*ptr!='\0');
return t;
}
The problem is that sizeof does not return the value you need, that function will return the size of the char *s (probably 4 or 8 -> bytes used to storage that pointer). Check the documentation links to understand more clearly.
One more thing, if you are doing that in order to practice your C skills is OK but if you are not, you will probable just want to use the strcpy function.
Hope it helps.
sizeof(s) returns the size of char *s which is 4 (on 32 bit) or 8 (on 64 bit) systems.
arrays and strings with size information gets degenerated to pointers losing its size attributes when it is passed as a parameter to a function
So when you are calculating the size of the parameter s it either returns 32/64 based on your bitness.
instead of sizeof, you should actually do strlen and add one to it to accommodate the null character.
instead of
char *t=malloc(sizeof(s));
try
char *t=malloc(strlen(s)+1);
Please note:
There are other design issues with your code
When passing a pointer argument which is not supposed to change, you should declare it const.
Generally returning an address of a locally generated heap storage is not a good practice and is the major cause of memory leak, if cal-lee ever forgets to free the storage. Instead pass it as a non-const parameter to the function.
sizeof returns the size of the pointer (usually 4 or 8 bytes), not the size of the pointed-to object. (There is no way to get at the latter information. sizeof is effectively a compile-time constant, by the way.)
s is a pointer to char, so malloc(sizeof(s)) allocates space for one pointer to char -- typically 2-8 bytes, most often 4 bytes. As it stands, it'll always allocate this fixed amount of space, regardless of the length of string you passed in. In your test, you're passing a much longer string than that, so you overflow the buffer you allocated.
You're already given the correct answer: under the circumstances, strlen is the right function to find the size.
malloc is declared in , so we #include that header in any program that calls malloc. A ``byte'' in C is, by definition, an amount of storage suitable for storing one character, so the above invocation of malloc gives us exactly as many chars as we ask for. We could illustrate the resulting pointer like this:
theMessageMaxLength keeps giving me a value of 4 even if the length is larger than 4. Why is this happening and how do I fix it? It is of type size_t and I don't have it declared anywhere, rather it is just a value being passed in as such:
place_value(int task, struct PDB *llist, char *theMessage, size_t theMessageMaxLength)
The above method is being called as follows:
place_value(task, llist, theMessage, sizeof(theMessage));
I'm assuming this is where the length gets set to 4, however, shouldn't it be set to something larger if my message is larger? How would I increase the size so it's not just 4...?
and then used like this within the function it is being passed into:
strncpy(llist->data1, theMessage, theMessageMaxLength);
llist->data1[theMessageMaxLength] = '\0';
It looks like you're confusing sizeof() with strlen().
sizeof(theMessage) will only give you the size of a char* which is a pointer - (4 bytes in your case). If you want the length of the string, you'll need to use strlen(theMessage) instead.
place_value(task, llist, theMessage, strlen(theMessage));
sizeof(theMessage) is literally same as sizeof(char *). Perhaps you were confused with the situation below:
char theMessage[1024];
/* ... some code here ...*/
printf("sizeof(theMessage): %zd\n", sizeof(theMessage));
If you allocate memory for theMessage, then you should provide its size.
EDIT: As a side node, you may be interested in strndup which allocates memory automatically and appends a NULL character at the end to the destination string. But of course, you'll have to be careful and don't forget to free it.
You are measuring the size of a pointer, which is 4 (on 32-bit platform).