Why does that works in c++ char arrays [duplicate] - c++

This question already has answers here:
No out of bounds error
(7 answers)
Closed 8 years ago.
I have the next code:
char arr[6] = "Hello";
strcpy(arr, "Hello mellow");
cout << strlen(arr) << ", " << arr << endl; // 12, Hello mellow
char arr1[] = "Hello";
strcpy(arr1, "Hello mellow");
cout << strlen(arr1) << ", " << arr1 << endl; // 12, Hello mellow
So, why does that work? Why doesn't it get limited somehow? Whatever I put instead of "Hello mellow", it works and prints it out.

It works because strcpy doesn't check that the destination array is at least as large as the source one. Your code invokes undefined behavior as you call strcpy with invalid arguments, and because the behavior is undefined, anything can happen; In your case, the memory is silently overwritten. Your program could crash as well.

In general, C and C++ don't check for boundaries (unlike other higher-level languages: Java, PHP, Python, Javascript, etc).
This means that if you try to strcopy, say, a 13-bytes string such as "Hello mellow" to a character array, it won't check whether or not the given array has been instantiated with enough memory to contain the string. It will just copy the given string, character by character, to the given memory pointer.
What happens here, is that you write at some places in memory you are not supposed to access; once in a while, this program might just crash, with no other indication than: segmentation fault.
If you happen to try this...
char arr1[8];
char arr2[8];
strcpy(arr1,"Hello mellow");
printf("%s\n", arr1);
printf("%s\n", arr2);
...it is very likely (but not 100% sure, see comments) you would get the following output:
Hello mellow
llow
Why? Because the second char[] would have been overwritten by the data you tried to put in the first one, without it having enough reserved space for it.
See: http://en.wikipedia.org/wiki/Stack_buffer_overflow

Native arrays in C/C++ are very low-level abstractions that are treated as pointers to memory locations in many use cases. So, when passing arr to strcpy, all strcpy knows is the address of arr[0]. As a result, there is no possibility of bounds checking. This is a very good thing for performance reasons. It is up to the programmer to ensure that he/she uses these low-level constructs safely, for instance by using strncpy and giving an appropriate bound, or using std::vector and checking for bounds explicitly or using std::vector::at to check bounds when accessing a location.

I think that's because there's no check runtime nor compile time and if you're Lucky, you won't get a segmentation fault;)

Related

Is character array size dynamic in C/CPP/C++?

My knowledge till now was that arrays in C and CPP/C++ have fixed sizes. However recently I encountered 2 pieces of code which seems to contradict this fact. I am attaching the pics here. Want to hear everyone's thoughts on how these are working. Also pasting the code and doubts here:
1.
#include <iostream>
#include <string.h>
using namespace std;
int main()
{
char str1[]="Good"; //size of str1 should be 5
char str2[]="Afternoon"; //size of str2 should be 10
cout<<"\nSize of str1 before the copy: "<<sizeof(str1);
cout<<"\nstr1: "<<str1;
strcpy(str1,str2); //copying str1 into str2
cout<<"\nSize of str1 after the copy: "<<sizeof(str1);
cout<<"\nstr1: "<<str1;
return 0;
}
your text
O/P:
Size of str1 before the copy: 5
str1: Good
Size of str1 after the copy: 5
str1: Afternoon
In first snippet I am using strcpy to copy char str2[] contents that is "Afternoon" into char str1[] whose size is 5 less than size of str2. So theoritically the line strcpy(str1,str2) should give error as size of str1 is less than size of str2 and fixed. But it executes, and more surprising is the fact that even after str1 contain the word "afternoon" the size is still the same.
2.
#include <iostream>
#include <cstring>
using namespace std;
int main()
{
char first_string[10]; // declaration of char array variable
char second_string[20]; // declaration of char array variable
int i; // integer variable declaration
cout<<"Enter the first string: ";
cin>>first_string;
cout<<"\nEnter the second string: ";
cin>>second_string;
for(i=0;first_string[i]!='\0';i++);
for(int j=0;second_string[j]!='\0';j++)
{
first_string[i]=second_string[j];
i++;
}
first_string[i]='\0';
cout<<"After concatenation, the string would look like: "<<first_string;
return 0;
}
O/P:
Enter the first string: good
Enter the second string: afternoon
After concatenation, the string would look like: goodafternoon
Here also even if I provide a string of length 20 as input to second_string[] it's still able to concatenate both the strings and put them in first_string[], even though the size of the concatenated string will be clearly greater than size of first_string[] which is 10.
I tried to assign a string of greater length to a string variable of smaller length. techincally it should not work but it worked anyway
There are two misunderstandings here
sizeof is the size of the array at compile time. It has nothing to do with the contents of the array. You can change the contents all you like and sizeof will still be the same. If you want the length of a string use the function strlen.
Most of the time when you break the rules of C++ it leads to undefined behaviour. Copying a string into an array that is too small to hold that string is one example of undefined behaviour.
You said
So theoritically the line strcpy(str1,str2) should give error as size
of str1 is less than size of str2 and fixed.
This is untrue. Undefined behaviour does not mean that there must be an error. It means exactly what it says, the behaviour of your program is undefined, anything could happen. That might mean an error message, or it might mean a crash, or it might mean that your program seems to work. The behaviour is undefined.
You aren't alone in thinking as you did. I reckon the purpose of sizeof and the nature of undefined behaviour are two of the commonest beginner misunderstandings.
And to answer the question in the title. The size of a character array is fixed in C++, nothing in your example contradicts that.
I've honestly never seen a C++ programmer write char stringname[20] = "string";, that just isn't the way you'd handle strings in C++⁰.
And neither would a C programmer use array notation, because well, it's just not common; you'd typically use arrays for things that aren't strings, even if the type of a "string literal" is actually char[length + 1].
Your access beyond the end of an array is simply a bug. It is undefined behaviour. A buffer overflow. A static code analyzer, quite possibly even a compiler, would tell you that this is a mortal sin. The str* functions know literally nothing about the size of your array, they only see a pointer to the first element, and your array literally knows nothing about the length of the string it contains, which is given by the terminating zero character's position. You're mixing up two things here!
In C++, you'd definitely use the std::string class to read from cin, exactly to avoid the problem with buffer overflows.
So, honestly: If you're a C++ beginner, maybe try to ignore C strings for now. It's not a C++ way of dealing with string data other than fixed string literals (i.e., things between "" in your source code), and the C way of string handling is literally still the dominant cause for remote-exploitable bugs in software, far as I can tell. C++ is not C, and, honestly, when it comes to handling strings, for the better. Including both <string.h> and <iostreams> is a pretty reliable indication of a programming beginner who has access to bad guides that treat C++ as extended C. But that's simply not true; it's a very different programming language with some far-reaching C compatibility, but you would, and should, not mix these two languages – as a beginner, it's hard enough to learn one¹.
⁰ Technically speaking, it even feels wrong; a string literal in C++ is a const char pointer, whereas it's just a char pointer in C. C and C++ are not the same language.
¹If you feel like you're explaining C++ to people, and sometimes feel overwhelmed with making a good explanation for things to people who are not expert C programmers already, Kate Gregory made a nice talk why teaching C to teach C++ is a really bad idea, which I agree to, even if she overstresses a few points.

Char array returns four times more data than expected

Before I continue, here's the code:
#include <iostream>
using namespace std;
int main() {
char array[] = {'a','b','c'};
cout << array << endl;
return 0;
}
My system:
VisualStudio 2019, default C++ settings
Using Debug build instead of release
When I run this code sample, I get something like this in my console output:
abcXXXXXXXXX
Those X's represent seemingly random characters. I know they're from existing values in memory at that address, but I don't understand why I'm getting 12 bytes back instead of the three from my array.
Now, I know that if I were doing this with ints which are four bytes long, maybe this would make sense but sizeof(array) returns three (ie. three bytes long, I know the sizeof(array) / sizeof(array[0] trick.) And when I do try it with ints, I'm even more confused because I get some four-byte hex number instead (maybe a memory address?)
This may be some trivial question, I'm sorry, but I'm just trying to figure out why it behaves like this. No vectors please, I'm trying to stay as non-STL as possible here.
cout takes this char array and addresses it as a null-terminated string.
Since the terminating character in this array is not the null character (i.e., char(0)), it attempts to print until encountering the null character.
At this point, it attempts to read memory outside of the array which you have allocated, and technically, anything could happen.
For example, there can be different data in that memory every time the function is called, or the memory access operation may even be illegal, depending on the address where array was allocated at the time the function was called.
So the behavior of your program is generally considered undefined (or non-deterministic).

Program creating file path using strdup and strcat crashes when fed more than 39 characters

I am trying to concatenate two char arrays using the function strcat(). However the program crashes.
#include <cstdio>
#include <cstring>
int main() {
const char *file_path = "D:/MyFolder/YetAnotherFolder/test.txt";
const char *file_bk_path = strcat(strdup(file_path), ".bk");
printf("%s\n", file_bk_path);
return 0;
}
The strangest thing to me is that the program indeed produces an output before crashing:
D:/MyFolder/YetAnotherFolder/test.txt.bk
What is the reason for this problem and how it can be fixed?
Error state is reproduced in Windows (MinGW 7.2.0).
strdup is creating new memory for you to hold a duplicate of the string. The memory is only as long as strlen(file_path) + 1. You then try to add an extra 2 characters into memory that you don't own. You will go out of range of the memory created and create some undefined behaviour. It might print because setting the memory and printing the first part could be happening correctly, but it is undefined and anything can happen. Also note, in strdup you need to call free on the memory it creates for you, or you are going to leak some memory.
Here is a much simpler way to do this, use a std::string:
const char *file_path = "D:/MyFolder/YetAnotherFolder/test.txt";
std::string file_bk_path = std::string(file_path) + ".bk";
std::cout << file_bk_path << "\n";
Here is a live example.
If it absolutely needs to be in C-style then you are better off controlling the memory yourself:
const char *file_path = "D:/MyFolder/YetAnotherFolder/test.txt";
const char *bk_string = ".bk";
char *file_bk_path = malloc((strlen(file_path) + strlen(bk_string) + 1)*sizeof(char));
if (!file_bk_path) { exit(1); }
strcpy(file_bk_path, file_path);
strcat(file_bk_path, bk_string);
printf("%s\n", file_bk_path);
free(file_bk_path);
Here is a live example.
As mentioned in the comments and answers, strdup mallocs the length of your path string, plus an extra cell for the string end character '\0'. When you concatenate to this two characters writing after the allocated area.
Following #Ben's comments, I'd like to elucidate some more:
To be clear strcat adds a delimiter, but this is already after the memory you were allocated.
In general unless you specifically hit no-no addresses, the program will probably run fine - in fact this is a common hard to find bug. If for example you allocate some more memory right after that address, you will be deleting said delimiter (so printing the string will read further into the memory.
So in general, you may be OK crash wise. The crash (probably) occurs when the program ends, and the OS cleans up the memory you forgot to free yourself - That extra cell is a memory leak, and will cause the crash. So you do get a full print, and only after a crash.
Of course all of this is undefined behavior, so may depend on the compiler and OS.

Purpose of char a[0] in converting integer to string using itoa()

I have this code,
char a[0];
cout << "What is a? " << a << endl;
char *word = itoa(123,a,10);
string yr = string(word);
but i have trouble comprehending the array a[0]. I tried to change its value and see if there is any changes, but it seems to make no differences at all.
example, even if a change a[0] to a[1], or any other integer, the output still make no difference
char a[1];
cout << "What is a? " << a << endl;
char *word = itoa(123,a,10);
string yr = string(word);
What is its purpose here?
Since itoa function is non-standard, this is a discussion of a popular signature itoa(int, char*, int).
Second parameter represents a buffer into which a null-terminated string representing the value is copied. It must provide enough space for the entire string: in your case, that is "123", which takes four characters. Your code passes a[] as the buffer, but the size of a[] is insufficient to accommodate the entire "123" string. Hence, the call causes undefined behavior.
You need to make a large enough to fit the destination string. Passing a buffer of size 12 is sufficient to accommodate the longest decimal number that can be produced by itoa on a 32-bit system (i.e. -2147483648). Replace char a[0] with char a[12] in the declaration.
What is its purpose here?
A zero-length array is an array with no elements in it.
You can't [legally] print or modify its contents, because it doesn't have any.
There are arcane reasons to want to use one, but speaking generally it has no purpose for you. It's not even allowed by the standard (although compilers tend to support it for those arcane reasons).
even if a change a[0] to a[1], or any other integer, the output still make no difference
Well, if you have an array with n elements in it, and you write more than n elements' worth of data to it, that's a "buffer overrun" and has undefined behaviour. It could appear to work as you overwrite somebody else's memory, or your program could crash, or your dog could suddenly turn into a zombie and eat you alive. Best avoided tbh.

Pointer increment and decrement

I was solving a question my teacher gave me and hit a little snag.
I am supposed to give the output of the following code:(It's written in Turbo C++)
#include<iostream.h>
void main()
{
char *p = "School";
char c;
c=++(*(p++));
cout<<c<<","<<p<<endl;
cout<<p<<","<<++(*(p--))<<","<<++(*(p++))<<endl;
}
The output the program gives is:
T,chool
ijool,j,i
I got the part where the pointer itself increments and then increments the value which the pointer points to. But i don't get the part where the string prints out ijool
Can someone help me out?
The program you showed is non-standard and ill-formed (and should not compile).
"Small" problems:
The proper header for input/output streams in C++ is <iostream>, not <iostream.h>
main() returns an int, not a void.
cout and endl cannot be used without a using namespace std; at the beginning of the file, or better: use std::cout and std::endl.
"Core" problems:
char* p = "School"; is a pointer to string litteral. This conversion is valid in C++03 and deprecated in C++11. Aside from that, normally string litterals are read only, and attempts to modify them often result in segfaults (and modifying a string litteral is undefined behvior by the standard). So, you have undefined behavior everytime you use p, because you modify what it points to, which is the string litteral.
More subtle (and the practical explanation): you are modifying p several times in the line std::cout<<p<<","<<++(*(p--))<<","<<++(*(p++))<<std::endl;. It is undefined behavior. The order used for the operations on p is not defined, here it seems the compiler starts from the right. You can see sequence points, sequence before/after for a better explanation.
You might be interested with the live code here, which is more like what you seemed to expect from your program.
Let's assume you correct:
the header to <iostream> - there is no iostream.h header
your uses of cout and endl with std::cout and std::endl respectively
the return type of main to int
Okay,
char *p = "School";
The string literal "School" is of type "array of 7 const char." The conversion to char* was deprecated in C++03. In C++11, this is invalid.
c=++(*(p++));
Here we hit undefined behaviour. As I said before, the chars in a string literal are const. You simply can't modify them. The prefix ++ here will attempt to modify the S character in the string literal.
So from this point onwards, there's no use making conjectures about what should happen. You have undefined behaviour. Anything can happen.
Even if the preceding lines were legal, this line is also undefined behavior, which means that you cannot accurately predict what the output will be:
cout<<p<<","<<++(*(p--))<<","<<++(*(p++))<<endl;
Notice how it modifies the value of p multiple times on that line (really between sequence points)? That's not allowed. At best you can say "on this compiler with this run-time library and this environment at this moment of execution I observed the following behavior", but because it is undefined behavior you can't count on it to do the same thing every time you run the program, or even if the same code is encountered multiple times within the same run of the program.
There are at least three problems with this code (and maybe more; I'm not a C++ expert).
The first problem is that string constants like should not be modified as they can be placed in read-only parts of the program memory that the OS maps directly to the exe file on disk (the OS may share them between several running instances of that same program for example, or avoid those parts of memory needing to be written to the swap file when RAM is low, as it knows it can get the original from the exe). The example crashes on my compiler, for example. To modify the string you should allocate a modifiable duplicate of the string, such as with strdup.
The second problem is it's using cout and endl from the std namespace without declaring that. You should prefix their accesses with std:: or add a using namespace std; declaration.
The third problem is that the order in which the operations on the second cout line happen is undefined behavior, leading to the apparently mysterious change of the string between the time it was displayed at the end of the first cout line and the next line.
Since this code is not intended to do anything in particular, there are different, valid ways you could fix it. This will probably run:
#include <iostream>
#include <string.h>
#include <stdlib.h>
using namespace std;
int main()
{
char *string = strdup("School");
char *p = string;
char c;
c=++(*(p++));
cout<<c<<","<<p<<endl;
cout<<p<<","<<++(*(p--))<<","<<++(*(p++))<<endl;
free(string);
}
(On my compiler this outputs: T,chool, diool,i,d.)
It still has undefined behavior though. To fix that, rework the second cout line as follows:
cout << p << ",";
cout << ++(*(p--)) << ",";
cout << ++(*(p++)) << endl;
That should give T,chool, chool,d,U (assuming a character set that has A to Z in order).
p++ moves the position of p from "School" to "chool". Before that, since it is p++, not ++p, it increments the value of the char. Now c = "T" from "S"
When you output p, you output the remainder of p, which we identified before as "chool".
Since it is best to learn from trial and error, run this code with a debugger. That is a great tool which will follow you forever. That will help for the second set of cout statements. If you need help with gdb or VS debugger, we can walk through it.