Why does this program NOT segfault? [duplicate] - c

This question already has answers here:
Accessing an array out of bounds gives no error, why?
(18 answers)
Closed 2 years ago.
Usually, this question is probably phrased in a positive way, becoming the next member in the club of duplicate questions - this one hopefully isn't. I have written a simple program to reverse a string in C. Here it is:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char arr[4] = "TEST";
char rev[2];
int j = 0;
for(int i = 0; i < 4; i++) {
j = 4 - i - 1;
rev[j] = arr[i];
}
printf("%s\n",rev);
}
When I define char arr and char rev to be of size 4, everything works fine. When I leave arr size out I get unexpected repeat output like "TSTTST". When I define rev to be an array of 2 chars, I do not get a segfault, yet in the loop I am trying to access its third and fourth element. As far as my relatively limited understanding tells me, accessing the third element in an array of length two should segfault, right? So why doesn't it?
EDIT:
Interestingly enough, when I leave the loop out like so
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char arr[4] = "TEST";
char rev[2] = "00";
printf("%s\n",rev);
}
it prints "00TEST". What happened here? Some kind of overflow? I even restarted the terminal, recompiled and ran again.
EDIT 2:
I have been made aware that this is indeed a duplicate. However, most of the suggested duplicates referred to C++, which this isn't. I think this is a good question for new C programmers to learn about and understand undefined behavior. I, for one, didn't know that accessing an array out of bounds does not always cause a SEGFAULT. Also, I learned that I have to terminate string literals myself, which I falsely believed was done automatically. This is partly wrong: it is added automatically - the C99 Standard (TC3) says in 6.4.5 String literals that terminating nulls are added in translation phase 7. As per this answer and the answers for this question, char arrays are also null-terminated, but this is only safe if the array has the correct length (string length + 1 for null-terminator).

char rev[2] assigns a memory of size 2*sizeof(char) with variable/pointer rev. You are accessing memory not allocated to the pointer. It may or may not cause errors.
It might appear to work fine, but it isn't very safe at all. By writing data outside the allocated block of memory you are overwriting some data you shouldn't. This is one of the greatest causes of segfaults and other memory errors, and what you're observing with it appearing to work in this short program is what makes it so difficult to hunt down the root cause.
When you do rev[2] or rev[3] you are accessing rev + 2 and rev + 3 addresses which are not allocated to rev pointer. Since its a small program and there is nothing there, it's not causing any errors.
In respect to edit:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char arr[4] = "TEST";
char rev[2] = "00";
printf("%s\n",rev);
}
%s prints till null is encountered, the size you have assigned of the arr and rev doesn't allow for null to be there, try changing values as follow:
char arr[5] = "TEST";
char rev[3] = "00";
The program will work as intended as in arr there will be TEST\0 and rev will be rev\0 where \0 is null character in C.
Give this article a read, it'll solve most of your queries.

Related

What if you dont allocate room for \0 using malloc?

From what I heard, I should allocate the memory like this in line 14:
array[i]=malloc(sizeof(char)*(strlen(buffer)+1)); I haven't added the 1 and still the code works perfect. I cant make it crash of return anything than 0. So, is the +1 needed or not? And if it is, what are the consequences going to be since my program runs smoothly?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 5
#define SIZE 512
int main(){
char *array[N];
char buffer[SIZE];
int i,j;
for(i=0;i<N;i++){
printf("Give word no.%d",i+1);
gets(buffer);
array[i]=malloc(sizeof(char)*strlen(buffer));
printf("%d",strlen(buffer));
if(!array[i]){
printf("Program will now exit.");
exit(0);
};
strcpy(array[i],buffer);
}
Tried it with both +1 and without. Same results although I've seen in tutorials that it is needed.
The +1 is needed to allocate room for the null terminator. If you don't, then you might write to the allocated array out of bounds, which is undefined behavior. What is undefined behavior and how does it work? Meaning it might seem to work just fine, to break mysteriously later on.
sizeof(char) is by definition always 1 though, so that part is not needed.
Recommendation: Use array[i] = malloc(strlen(buffer) + 1);
As a side note, whoever (book/teacher) told you to use gets should be retired and not be used as a source of learning C programming. Why is the gets function so dangerous that it should not be used?

Why don't c compiler throws error if I try storing values greater than what I specified during dynamic allocation?

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.

Explanation on how does the memcpy function behaves? [duplicate]

This question already has answers here:
No out of bounds error
(7 answers)
Closed 5 years ago.
#include <stdio.h>
#include <string.h>
char lists[10][25];
char name[10];
void main()
{
scanf("%s" , lists[0]);
memcpy(name , lists[0], 25);
printf("%s\n" , name);
}
In the above code I am predefining the size of character array "name" as 10.
Now when I gave the input as :
Input - abcdefghijklmnopqrstuvwxy
The output I got was the same string : abcdefghijklmnopqrstuvwxy
Should'nt I get the output as : abcdefghij ???
how this is becoming possible even though the size of array is limited to 10?
Because it doesn't know the size of the allocated memory it's writing into, and you got away with where the extra data got written. You might not on another platform, or using a different compiler, or different optimisation settings.
When passing the size parameter to memcpy (), it's a good idea to take the size of the destination memory into account.
When using char arrays, if you want to be safer about not overrunning memory, you can use strncpy (). It'll take care of inserting the trailing NULL in the right place.
To start with, arrays are pointers. In C there are no length checks like on Java for example.
When you write char a[2]; the OS gives you space on the memory for 2 chars.
For example, let the memory be
|1|2|3|4|5|6|7|8|9|10|11|12|
a
a is a pointer to the address 1. The a[0] = 0; is equal with *(a+0) = 0, meaning write 0 to the address a + offset 0.
So if you try to write to an address that you have not allocated, unexpected things can happen.
For example, lets say we have char a[2];char b[2]; and the memory map is
|1|2|3|4|5|6|7|8|9|10|11|12|
a b
Then the a[2] = 0 is equal to b[0] = 0. But if this address is an address of an other program, then a segmentation error will be raised.
Try the program (it may work with no optimizations of the compiler):
#include <stdio.h>
#include <string.h>
char a[4];
char b[4];
void main()
{
scanf("%s" , a); // input "12345678"
printf("%s\n" , b); // print "5678"
}
memcpy just copies from an address to the other the size of data you said.
In your example, you were luky because all the addresses you accessed where assigned to your program (inside your's memory page).
In C/C++ you are responsible to handle the memory correctly. Also, keep in mind that strings end at the char \0 so inside an array char str[10]; we usually have tops 9 chars and the \0.

strcpy issue with char arrays in structs in C

So I'm working on a program to take in assembly code in a text file and produce the corresponding machine code. However, I'm running into an issue when I'm trying trying to assign values to the members of the AssemblyLine struct. What happens is that when ".fill" is the opcode, arg0 is concatenated to it, and there are also issues with arg0 if I assign value to arg0 first. It is important to note that this only happens when the opcode is ".fill". For example, if the opcode is "add" the values are what I intended for them to be.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct AssemblyLine {
char opcode[5];
char arg0[7];
char arg1[7];
char arg2[7];
_Bool initialized;
};
struct Label {
char name[7];
int address;
_Bool initialized;
};
main()
{
struct AssemblyLine line;
strcpy(line.opcode, ".fill");
strcpy(line.arg0, "5");
printf("%s\n", line.opcode);
return 0;
}
The output for this program is:
.fill5
My intention is that the output would just be:
.fill
I'm really confused about what would be causing this. Sorry if the answer is really obvious, this is my first time working in C, though I have programmed in C++ before. I was at first thinking that there was no null terminating character, but the string is read fine until after I use the second strcpy. Is fill used as a key word for strcpy or something? I thought maybe it had to do with the '.' but that didn't affect anything when the opcode was ".lw".
Sorry that this post is so long! Thanks for any help!
Your array isn't big enough. ".fill" is six characters include the terminating null, but you only allocate memory for five with char opcode[5]. You need to make your array bigger.
The string ".fill" is 5 characters + 1 zero character long. That makes 6 characters.
But the array 'opcode' is only 5 characters long, so the trailing zero is written to 'arg0'.
After that, your copy "5" (2 characters with zero) to 'arg0'.
Because 'printf' prints until the trailing zero occurs, it reads out of the bounds of opcode.

Segmentation fault (core dumped) and zlib

I am very new to both using Linux and creating anything remotely serious on C. I've been trying to create a program which will simply compress a single string, but I keep getting this Segmentation fault when trying to run the compiled file.
I compiled it using:
gcc 2.c -o test.o -lz
My code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
#include <assert.h>
int main ()
{
char *istream = malloc(10), *ostream = malloc(120);
istream = "istream";
int res = compress(ostream, (uLongf *)strlen(ostream), istream,(ulong)strlen(istream));
return 0;
}
Could anyone explain to me why this error is happening and how can I improve my code?
This line would appear to be the main problem:
(uLongf*)strlen(ostream)
You are interpreting a size_t value as a pointer to a buffer. You are meant to pass the address of an unsigned long containing the length of the output buffer. Take another look at the documentation for compress.
On top of that you don't yet understand how C strings work. The assignment operator when used with a char* lvalue merely copies an address and not the contents of a string. I suggest that you declare your buffers like this:
const char *istream = "istream";
char ostream[120];
I think your program should be something along these lines:
int main(void)
{
const char *istream = "istream";
char ostream[120];
uLongf destLen = sizeof(ostream);
int res = compress((Bytef*)ostream, &destLen, (Bytef*)istream, strlen(istream));
return 0;
}
Note that I wrote the code assuming that you are using a C compiler. And hence int main(void).
First you make istream point to memory you allocate:
char *istream = malloc(10)
then you make it point to a literal (and therefore constant and read-only) string:
istream = "istream";
You need to copy the string literal into the allocated memory, or you will no longer have the original pointer you allocated and have a memory leak. You also will not be able to free that pointer, since istream points to something you haven't allocated with malloc.
As for the crash, see the answer by David Heffernan.
As a side-note, there is no C++ in your code, only pure and plain C.
Change:
istream = "istream"
To
strcpy(istream,"istream");
In addition, what did you expect strlen(ostream) to return? 120?
strlen returns the index of the first 0 character encountered within the input string.
In your case, the contents of the memory pointed by ostream is unknown (i.e. "junk").
strlen will scan this memory until a 0 character is encountered, but will probably exceed the 120-byte memory space and cause a memory access violation.
Change strlen(ostream) to 120 if that was your intention.

Resources