I'm trying to mimic the way strcpy works, by writing my own function. My question is more specific to the size of an array.
I'm trying to copy contents of stringA into stringB, which I have declared as size of 1.
Shouldn't I get an error saying stringB is too small to hold stringA? The reason, I suspect I don't get an error is because an array name StringB is converted to a pointer?
Why is char str[1] = "abcd" illegal then ?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void strCopy(char *source,char *destination) {
printf("%p,%p\n",source,destination);
while (*source != '\0') {
*destination = *source;
printf("> %c,%p,%p\n",*source,source,destination);
source++;
destination++;
}
*destination = '\0';
}
int main(int argc,char *argv[]) {
char stringA[] = "To be or not to be.";
char stringB[1];
strCopy(stringA,stringB);
printf("StringB: %s\n",stringB);
return (0);
}
1) Shouldn't i get an error saying stringB is too small to hold stringA ?
you didn't get any error because of compilers memory allocation scheme. there is a good chance that compiler would have allocated memory to stringB first and stringA later. so consider if stringB is having memory location 1000 (only one byte), then stringA will be having memory locations from 1001 to 1020(including space for NULL). so when ever you try to copy contents from stringA to stringB into your function, it actually copies from location 1001 to 1000, then 1002 to 1001 and so on, so according to your program you must be having output as shown if this is the case. it seems that you are printing addresses of strings. see addresses of strings and check if this is the case. again this is merely consequence that string2 is allocated memory first than stringA. it is not neccesary that on all the variations of compilers, this will be the answer everytime.
stringB : To be or not to bee
stringA : o be or not to bee
now you are not getting error because both arrays are present in stack frame and both are accessed by pointers, so compiler doesn't care about sizes of array when data copy is done using pointers. it just copies till you are in allowed memory space. if this is not the case and stringA would have been present first in memory, then you would get segmentation fault.
2) Why is char str[1] = "abcd" illegal then ?
not this is very different case than your case, you are allocating only one byte to string, and initializing it with more characters than its size. note that your data copy is being done at run time, but this initialization is at compile time, so compiler knows size of array and will show you a warning, it is not illegal but compiler only assigns first character to string and you may get unpredictable result as there no space left for NULL character at last.
1) Shouldn't i get an error saying stringB is too small to hold stringA ?
You may not get an error depending on where in the memory space stringB was placed. If the stringB pointer was placed in the middle of the application's memory then you can certainly write values past its declared bounds. You'll just be overwriting memory that was used to store some other value or function. C/C++ never checks if you went past the array length like some other languages do, e.g. Java.
2) Why is char str1 = "abcd" illegal then ?
Since the behavior of your program is completely undefined based on your implementation it is difficult to describe exactly what is going on.
Also see the following post: why doesn't my program crash when I write past the end of an array?
Related
#include <stdio.h>
int main ()
{
char str[40];
printf("Enter a string : \n");
gets(str);
printf("You entered: %s\n", str);
return 0;
};
in above code, if replace str to a pointer, char *str. Then NULL is out. Suppose gets defined by char *gets(char *str), it should use a pointer instead of array. All examples I saw are array not pointers. Thanks.
function gets() is depracted your libc/compiler might ignore it. try use fgets() instead.
#include <stdio.h>
int main ()
{
char str[40];
printf("Enter a string : \n");
if (fgets(str, sizeof(str), stdin) != NULL)
{
printf("You entered: %s\n", str);
}
return 0;
};
also if you want to don't use stack you need to give pointer that points allocated space. in code str also can be char *str = malloc(40); then change sizeof(str) to 40 since str is no longer stack.
Really interesting question, I have been asked this question a lot!
you should have a bit background of pointers and memory to understand what is happening.
first let's have a brief review about pointers and memory:
our computer have some memory and we can use it in programming, anything that we store (in runtime) for example an int, array of doubles, some complex struct and strings(that they are array of characters) should be somewhere in memory.
pointers contain address of somewhere in memory, some of them know about that memory (how to read/write value) some of them don't.
there is a special value for pointers (NULL) that means nowhere, if pointer is pointing to NULL, that pointer is pointing not nowhere (obviously nowhere is not a valid address in memory)
array is specific type of pointer, a const pointer that is pointing to already allocated memory in stack.
and about gets function: let's think we want to re-implement such function (namely my_gets) , how we suppose to do that? how to return a string (array of characters)?
these are options (as far as i know):
creating a local array in our function and fill it. then we should return it? no we cant! because that array is in stack of our function and after ending the function, our function data including this array will be popped automatically (handled by compiler).
although nobody forbid us from returning that array, but that would cause dangling pointer problem.
allocating some space rather than stack (heap) and fill that. this is perfectly fine and there is methods and do this! for example readline (not in ansi c, you can find it here) will do this. the drawback of this method is that you should take care of that memory and free it later, it also may be not to optimum way and you may should copy that string to your already allocated memory
the last way (and way that gets use) is getting a pointer that is already pointing to a valid memory and fill that memory. you already know that gets want a pointer as input, I add that, that pointer should point to a valid and accessible memory that gets can fill it. if pointer is pointing to NULL (or maybe uninitialized and pointing to some where random) gets will fail writing and cause undefined behavior (segmentation fault for example)
some final points:
array solution work because array name is pointer that pointing to valid memory (array in stack) so it's OK and easy to understand.
If we don't want to use array, we can point our pointer to a valid memory, we need to use malloc/calloc to allocate a block of memory. see this:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int size = 40 * sizeof(char);
char* p = malloc(size);
printf("Enter a string : \n");
if (fgets(p, size, stdin) != NULL) {
printf("You entered: %s\n", p);
}
free(p);
return 0;
}
gets is not secure because it doesn't care how much memory we have, it writes until and string ends and it may cause buffer overflow, better option (as people said) is fgets because it care memory size and will not exceed that. but my answer doesn't care it's fgets or gets.
This is what I have tried. I have not even ended my string with a \0 character.
#include <stdio.h>
#include <malloc.h>
#include <string.h>
int main()
{
int size=5;
char *str = (char *)malloc((size)*sizeof(char));
*(str+0) = 'a';
*(str+1) = 'b';
*(str+2) = 'c';
*(str+3) = 'd';
*(str+4) = 'e';
*(str+5) = 'f';
printf("%d %s", (int)strlen(str), str);
return 0;
}
According to the rule, it can store only 4 charaters and one for the \0 as I have specified it in malloc.
It gives me the perfect output.
Output
6 abcdef
Check this out here : https://onlinegdb.com/B1UeOXbjH
You allocated your own memory, so it is up to you to manage it responsibly. In your example, you allocated 5 bytes of RAM, and you created a pointer which points to the first address of it. Your pointer is not a string, it is not an array. So, what you then did was you wrote 6 bytes, starting at the address pointed to by your pointer. The 6th byte is overflowing into unallocated memory. So you wrote it into memory which may be used for something else and could cause unknown problems. You have created a leak, and you didn't free up the memory you allocated when you quit the program, which is another leak. You didn't add in a /0 anywhere so I honestly think you lucked out. There really isn't any way to know how strlen() would respond. If you want C to handle it for you, than you have char *str = "abcdef" and that will create your string of length 6 plus the /0. But if you do it manually like you did, than you have to handle everything.
C does not count its arrays: if you ask for a chunk of memory of so-many bytes, it gives you that chunk via a pointer, and it's entirely up to you to manage it responsibly.
This leads to a certain efficiency - no overhead from the compiler/runtime checking all this for you - but creates enormous challenges for incorrect code (which you've shown an example of).
Many of us every much like the down-to-the-metal efficiency of C, but there's a reason that so many prefer languages such as Java or C# that do manage this for you, and enforce array bounds. It's a tradeoff.
So I read that strcat function is to be used carefully as the destination string should be large enough to hold contents of its own and source string. And it was true for the following program that I wrote:
#include <stdio.h>
#include <string.h>
int main(){
char *src, *dest;
printf("Enter Source String : ");
fgets(src, 10, stdin);
printf("Enter destination String : ");
fgets(dest, 20, stdin);
strcat(dest, src);
printf("Concatenated string is %s", dest);
return 0;
}
But not true for the one that I wrote here:
#include <stdio.h>
#include <string.h>
int main(){
char src[11] = "Hello ABC";
char dest[15] = "Hello DEFGIJK";
strcat(dest, src);
printf("concatenated string %s", dest);
getchar();
return 0;
}
This program ends up adding both without considering that destination string is not large enough. Why is it so?
The strcat function has no way of knowing exactly how long the destination buffer is, so it assumes that the buffer passed to it is large enough. If it's not, you invoke undefined behavior by writing past the end of the buffer. That's what's happening in the second piece of code.
The first piece of code is also invalid because both src and dest are uninitialized pointers. When you pass them to fgets, it reads whatever garbage value they contain, treats it as a valid address, then tries to write values to that invalid address. This is also undefined behavior.
One of the things that makes C fast is that it doesn't check to make sure you follow the rules. It just tells you the rules and assumes that you follow them, and if you don't bad things may or may not happen. In your particular case it appeared to work but there's no guarantee of that.
For example, when I ran your second piece of code it also appeared to work. But if I changed it to this:
#include <stdio.h>
#include <string.h>
int main(){
char dest[15] = "Hello DEFGIJK";
strcat(dest, "Hello ABC XXXXXXXXXX");
printf("concatenated string %s", dest);
return 0;
}
The program crashes.
I think your confusion is not actually about the definition of strcat. Your real confusion is that you assumed that the C compiler would enforce all the "rules". That assumption is quite false.
Yes, the first argument to strcat must be a pointer to memory sufficient to store the concatenated result. In both of your programs, that requirement is violated. You may be getting the impression, from the lack of error messages in either program, that perhaps the rule isn't what you thought it was, that somehow it's valid to call strcat even when the first argument is not a pointer to enough memory. But no, that's not the case: calling strcat when there's not enough memory is definitely wrong. The fact that there were no error messages, or that one or both programs appeared to "work", proves nothing.
Here's an analogy. (You may even have had this experience when you were a child.) Suppose your mother tells you not to run across the street, because you might get hit by a car. Suppose you run across the street anyway, and do not get hit by a car. Do you conclude that your mother's advice was incorrect? Is this a valid conclusion?
In summary, what you read was correct: strcat must be used carefully. But let's rephrase that: you must be careful when calling strcat. If you're not careful, all sorts of things can go wrong, without any warning. In fact, many style guides recommend not using functions such as strcat at all, because they're so easy to misuse if you're careless. (Functions such as strcat can be used perfectly safely as long as you're careful -- but of course not all programmers are sufficiently careful.)
The strcat() function is indeed to be used carefully because it doesn't protect you from anything. If the source string isn't NULL-terminated, the destination string isn't NULL-terminated, or the destination string doesn't have enough space, strcat will still copy data. Therefore, it is easy to overwrite data you didn't mean to overwrite. It is your responsibility to make sure you have enough space. Using strncat() instead of strcat will also give you some extra safety.
Edit Here's an example:
#include <stdio.h>
#include <string.h>
int main()
{
char s1[16] = {0};
char s2[16] = {0};
strcpy(s2, "0123456789abcdefOOPS WAY TOO LONG");
/* ^^^ purposefully copy too much data into s2 */
printf("-%s-\n",s1);
return 0;
}
I never assigned to s1, so the output should ideally be --. However, because of how the compiler happened to arrange s1 and s2 in memory, the output I actually got was -OOPS WAY TOO LONG-. The strcpy(s2,...) overwrote the contents of s1 as well.
On gcc, -Wall or -Wstringop-overflow will help you detect situations like this one, where the compiler knows the size of the source string. However, in general, the compiler can't know how big your data will be. Therefore, you have to write code that makes sure you don't copy more than you have room for.
Both snippets invoke undefined behavior - the first because src and dest are not initialized to point anywhere meaningful, and the second because you are writing past the end of the array.
C does not enforce any kind of bounds checking on array accesses - you won't get an "Index out of range" exception if you try to write past the end of an array. You may get a runtime error if you try to access past a page boundary or clobber something important like the frame pointer, but otherwise you just risk corrupting data in your program.
Yes, you are responsible for making sure the target buffer is large enough for the final string. Otherwise the results are unpredictable.
I'd like to point out what is actually happening in the 2nd program in order to illustrate the problem.
It allocates 15 bytes at the memory location starting at dest and copies 14 bytes into it (including the null terminator):
char dest[15] = "Hello DEFGIJK";
...and 11 bytes at src with 10 bytes copied into it:
char src[11] = "Hello ABC";
The strcat() call then copies 10 bytes (9 chars plus the null terminator) from src into dest, starting right after the 'K' in dest. The resulting string at dest will be 23 bytes long including the null terminator. The problem is, you allocated only 15 bytes at dest, and the memory adjacent to that memory will be overwritten, i.e. corrupted, leading to program instability, wrong results, data corruption, etc.
Note that the strcat() function knows nothing about the amount of memory you've allocated at dest (or src, for that matter). It is up to you to make sure you've allocated enough memory at dest to prevent memory corruption.
By the way, the first program doesn't allocate memory at dest or src at all, so your calls to fgets() are corrupting memory starting at those locations.
I am trying to print an array of structs and receiving an access violation. Not sure how to fix this. i, j, k are 0 in at the break. Any help is appreciated.
Update:Rephrasing the question, What is the correct way to access the data inside the struct? Also qwr's answer looks promising, I will have time to test later, thanks.
About access violation:
Access Violation is generally an attempt to access memory that the CPU cannot physically address.
Common causes:
Dereferencing NULL pointers . (THIS IS YOUR CASE see picture). You want to access to 0x000(NULL) location
Attempting to access memory the program does not have rights to (such
as kernel structures in process context)
Attempting to access a
nonexistent memory address (outside process's address space)
Attempting to write read-only memory (such as code segment)
More :more here in wikipedia
How you should allocate (using your structure):
First You should allocate memory;And dont forget you should write its free function,too
Simple allocation would be such:
//instead I advice PCurrentData notation
CurrentData AllCurrentData=malloc(NumProduct* sizeof *CurrentData);
//memset so we can then check it with null
memset(AllCurrentData,0,NumProduct* sizeof *CurrentData);
int i,y;
for(i=0;i<NumProduct;i++){
//Noncontiguous allocation.Suits to string allocation,cause string lens differs
AllCurrentData[i].data=malloc(COLUMNS*sizeof(char**));
memset(AllCurrentData[i].data,0,COLUMNS*sizeof(char**));
for(j=0;j<COLUMNS;j++){
//this is just example
//probably you want to allocate null terminating string
int size=strlen(your_null_terminating_string)+1;//+1 for adding null,too
AllCurrentData[i].data[j]=malloc(size*sizeof(char));
memcpy(AllCurrentData[i].data[j],your_null_terminating_string,size);
}
}
Additionaly, What else is wrong in your code and proper way
Plus you are doing wrong. Looking at your structure you hold char**.But you try to access it with 2D [][] array. Why we can not? Cause there is no guarantee that char** reference to continguous allocation, or only it done ,by manually, the way that compiler can access to it with [][].
Plus for char** mostly allocation done with noncontinguous way.cause strings len differs
So Note these:
char** is not equal to 2D array char v[][] array. So doing AllCurrentData[i].data[j][k] is wrong to address char**.
only char* can be equally treated as one dimensional char v[] array. char[] decay to char*
So accessing should be done this way:
#define NULL 0
if(AllCurrentData[i]!=NULL && AllCurrentData[i].data[j]!=NULL){
char* str=AllCurrentData[i].data[j];
if(str!=NULL)
while (str[k]!='\0'){
printf("%c ",str[k]);++k;
}
}
/*or
while (*str){
printf("%c ",*str);++str;
} */
/*but actualy you can print null terminating string directly
(assuming you wanted this)
if(str!=NULL)
printf("%s",str);
*/
more to learn about arrays and pointers
I found this code working perfectly.
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
char* s; /* input string */
s=malloc(sizeof(s));
int c;
if(argc==1){ // if file name not given
while (gets(s)){
puts(s);
}
}
}
What I don't understand is, how is the string s stored in memory.i am allocating memory only for the pointer s, which is of 4 bytes.Now where does the input string given by the user get stored in?
It's only safe for the first four bytes. The fifth byte will overrun the allocated data and tramp on something else which will yield undefined behaviour (might crash, might not).
Also, you don't null terminate the string with '\0' after you finish writing the chars, so you'll probably introduce another crash when you try and call a string routine (strcpy) on it - unless the memory after your string happened to contain zeros anyway, but naturally you shouldn't rely on this chance!
Rather than this you should do
s=malloc(sizeof(*s)*(number_of_chars+1));
You set number_of_chars to appropriate value, so that you allocate memory to store those many characters. +1 is for last '\0' character.
With your approach you are allocating 4 bytes so you can store usually those many characters.
You've allocated sizeof(void*) bytes of memory and filling it with user-provided data. You have an address and writing to it, it's ok from compiler's point of view (maybe it's really what you want, who knows). Even if you program didn't crash when you exceed it - it's still an error. It's just a memory, something else could be stored in this area, and you'll overwrite it - so expect heavy trouble if you'll ever do that.
It's possible as compiler assigns two bytes.
now you give 10 bytes in input, so your allocated memory overflows and data stored beyond your allocated memory only if its available.
It might give error if the data you want to store is greater then available and not give error if the data you want to store is greater then allocated.
puts will print data until it gets '\0'.
So this is expected behavior!!