Increment pointer address and value in C - c

int main (void){
int num1=2;
int *pnum=NULL;
pnum=&num1;
*pnum++;
printf("%d",*pnum);
}
why does this code print the address, not the value? Doesn't * dereference the pnum?

Check the precedence of operators: postfix unary operators bind tighter than prefix unary operators, so *pnum++ is equivalent to *(pnum++), not to (*pnum)++.
pnum++ increments the pointer pnum and returns the old value of pnum. Incrementing a pointer makes it point to the next element of an array. Any variable can be treated as an array of one element, so pnum points to the element after the first in the one-element array located where num1 is, which I'll call num1_array[1]. It is valid for a pointer to point to the end of an array, i.e. one position past the last element. It is not valid to dereference that pointer: that would be an array overflow. But it is valid to calculate the pointer. Constructing an invalid pointer in C is undefined behavior, even if you don't dereference it; however this pointer is valid.
*pnum++ dereferences the old value of pnum. Since that was a pointer to num1, this expression is perfectly valid and its value is the value of num1. At this point, any halfway decent compiler would warn that the value is unused. If you didn't see this message, configure your compiler to print more warnings: unfortunately, many compilers default to accepting bad code rather than signal the badness. For example, with GCC or Clang:
$ gcc -Wall -Wextra -Werror a.c
a.c: In function ‘main’:
a.c:6:5: error: value computed is not used [-Werror=unused-value]
6 | *pnum++;
| ^~~~~~~
cc1: all warnings being treated as errors
The call to printf receives the argument *pnum. We saw before that at this point, pnum points to the end of the one-element array num1_array[1]. This pointer is valid, but since it points to the end of an object, dereferencing has undefined behavior. In practice, this usually either crashes or prints some garbage value that happens to be in a particular memory location. When you're debugging a program, there are tools that can help by making it more likely that an invalid pointer will cause a crash rather than silently using a garbage value. For example, with GCC or Clang, you can use AddressSanitizer:
$ export ASAN_OPTIONS=symbolize=1
$ gcc -Wall -Wextra -fsanitize=address a.c && ./a.out
a.c: In function ‘main’:
a.c:6:5: warning: value computed is not used [-Wunused-value]
6 | *pnum++;
| ^~~~~~~
=================================================================
==2498121==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff15ae3e74 at pc 0x55d593978366 bp 0x7fff15ae3e30 sp 0x7fff15ae3e20
READ of size 4 at 0x7fff15ae3e74 thread T0
#0 0x55d593978365 in main (/tmp/stackoverflow/a.out+0x1365)
#1 0x7f525a1380b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)
#2 0x55d59397818d in _start (/tmp/stackoverflow/a.out+0x118d)
Address 0x7fff15ae3e74 is located in stack of thread T0 at offset 36 in frame
#0 0x55d593978258 in main (/tmp/stackoverflow/a.out+0x1258)
This frame has 1 object(s):
[32, 36) 'num1' (line 3) <== Memory access at offset 36 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow (/tmp/stackoverflow/a.out+0x1365) in main
Shadow bytes around the buggy address:
0x100062b54770: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100062b54780: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100062b54790: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100062b547a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100062b547b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100062b547c0: 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1[04]f3
0x100062b547d0: f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100062b547e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100062b547f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100062b54800: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100062b54810: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==2498121==ABORTING
This trace tells you:
That there was a buffer overflow in a local variable (stack-buffer-overflow).
That the overflowing access was an attempt to read 4 bytes (READ of size 4).
Some more information about the problematic address ([32, 36) 'num1'). You can see that the program tried to access memory just after num1.
The address of the problematic instruction (#0 0x55d593978365). You can set a breakpoint there in a debugger to examine what the program might be doing.
On most platforms, given your program, num1 is a variable on the stack, and the end of num1 is the address of the previous variable on the stack. This could be anything, depending on the details of how your compiler accesses memory. One of the many things this could be is pnum, if pnum and num1 happen to have the same size on your platform (this is typically the case on 32-bit platforms) and the compiler decides to put pnum just before num1 on the stack (this depends heavily on the compiler, the optimization level, and fine details of the program). So it is plausible for your program to print the address of pnum: not because *pnum somehow didn't invoke the dereference operator, but because your program has made pnum point to itself.

Postfix operators always have higher precedence than prefix operators in C. So *pnum++ is equivalent to *(pnum++) -- it increments the pointer, not the value pointed at.
You need (*pnum)++ or ++*pnum if you want to increment the pointed at value.

You need to put parenthesis around *pnum. Otherwise the address which the pointer points to is changed. pnum++ is pointer arithmetic and increments the pointer by the number equal to the size of the data type for which it is a pointer, in this case sizeof(int). This means it doesn't point to the correct value anymore and the program therefore prints “garbage” as mentioned by Eugene Sh. in the comments, because it dereferences the incremented pointer using *(pnum++).
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int num1 = 2;
int *pnum = &num1;
(*pnum)++; /* important */
printf("%d\n", *pnum);
return EXIT_SUCCESS;
}

When you write *pnum++; There, you change the address pnum is pointing. And incrementing it by one, we don't really know where it has started to point. As suggested above, it's referencing garbage right now.

Related

gcc -O2 creates an endless loop, probably because of undefined behaviour

I wrote this C code to solve Advent of Code 13 2020. I know, it's probably not viable trying to solve it via brute force, but the program gives the correct answer for the example input.
If I try to let gcc optimize the code, it gives the correct result with -O1, but creates an endless loop with -O2. After all the research my conclusion is that there is undefined behavior in my code, I guess it has to do with the probability that "found" may never be higher than 0 and so "time" would overflow.
Here is the question: Does anybody know how to patch that undefined behavior?
"-Wall -Wextra -pedantic" don't even issue a warning or something.
I just can't find a solution. If I, for example, change the head of the while loop to (!found && time < 10000000000), so that no overflow can occur, it just breaks the loop right away with a time value of 10000000003 when compiled with -O2, but still gives the right result with -O1.
Here is the code, the correct result would be "1068781":
#include <stdio.h>
int main()
{
unsigned int busses[] = {7, 0, 13, 2, 59, 1, 31, 0, 19};
unsigned int busses_used = 9;
unsigned int i = 0;
unsigned int found = 0;
unsigned long long time = 0;
unsigned int offset = 0;
unsigned int increment = 7;
while (!found) {
time += increment;
offset = 0;
for (i = 0; i < busses_used; i++) {
if ((time + offset) % busses[i] == 0) {
found = 1;
offset++;
} else {
found = 0;
break;
}
offset += busses[++i];
}
}
printf("Endtime: %lld\n", time);
return 0;
}
Edit: Thanks to KamilCuk for pointing out that the code is accessing the array out of bounds and teaching how to find out that it is doing so. The problem is solved by adding another 0 at the end of the "busses" array and therefore also setting "busses_used" to 10 instead of 9.
I do not understand the code and not indent to, but the loop is strange. Anyway:
"-Wall -Wextra -pedantic" don't even issue a warning or something.
And there are also other ways to detect UB! Compiling your code with some more -fsanitize=* options results with:
+ gcc -Wall -Wextra -ggdb3 -fsanitize=address -fsanitize=undefined -fsanitize=pointer-compare -fsanitize=pointer-subtract -fsanitize-address-use-after-scope /tmp/1.c
/tmp/1.c:22:29: runtime error: index 9 out of bounds for type 'unsigned int [9]'
/tmp/1.c:22:29: runtime error: load of address 0x7ffc48c7a894 with insufficient space for an object of type 'unsigned int'
0x7ffc48c7a894: note: pointer points here
13 00 00 00 60 60 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 08 aa c7 48 fc 7f 00 00
^
=================================================================
==73835==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffc48c7a894 at pc 0x55c28a5bf773 bp 0x7ffc48c7a800 sp 0x7ffc48c7a7f0
READ of size 4 at 0x7ffc48c7a894 thread T0
#0 0x55c28a5bf772 in main /tmp/1.c:22
#1 0x7f0060bd1151 in __libc_start_main (/usr/lib/libc.so.6+0x28151)
#2 0x55c28a5bf12d in _start (/tmp/tmp.qv8ZsZofOJ.out+0x112d)
Address 0x7ffc48c7a894 is located in stack of thread T0 at offset 84 in frame
#0 0x55c28a5bf208 in main /tmp/1.c:3
This frame has 1 object(s):
[48, 84) 'busses' (line 4) <== Memory access at offset 84 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow /tmp/1.c:22 in main
Shadow bytes around the buggy address:
0x1000091874c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000091874d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000091874e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1000091874f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100009187500: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 f1 f1 00 00
=>0x100009187510: 00 00[04]f3 f3 f3 f3 f3 00 00 00 00 00 00 00 00
0x100009187520: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100009187530: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100009187540: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100009187550: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100009187560: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==73835==ABORTING
After quick inspection the out of bounds access to busses happens here:
offset += busses[++i];
Does anybody know how to patch that undefined behavior?
No idea, but write an algorithm that doesn't access the array out of bounds.

Is there a solution to detect memory error in a C program?

I'm coding C lib functions and have a problem about the memory.
For the strcpy() function:
char *my_strcpy(char *str1, char *str2)
{
int i = 0;
for (; str2[i] != 0; i++)
str1[i] = str2[i];
str1[i] = 0;
return (str1);
}
With the Criterion test below:
#include <criterion/criterion.h>
char *my_strcpy(char *str1, char *str2);
Test(my_strcpy, in_allocated_string)
{
char *src = "Hello World";
char dest[11];
my_strcpy(dest, src);
cr_assert_str_eq(dest, "Hello World");
cr_assert_eq(dest, my_strcpy(dest, src));
}
The destination buffer is smaller than the source so it should not work... But it works. Valgrind or scan build don't give me any error, it compiles and runs without error...
A Makefile for compile and run with test:
SRC = code.c \
SRC_TEST = test.c \
LDFLAGS = -L./lib/my -lmy
OBJ = $(SRC:.c=.o)
CC = gcc
CFLAGS = -W -Wall -Wextra -Werror -fstack-protector -fstack-protector-all -fstack-protector-strong -Wstack-protector
NAME = libmy.a
all: $(NAME)
$(NAME): $(OBJ)
ar rc $# $^
test: $(SRC) $(SRC_TEST)
$(CC) -fprofile-arcs -ftest-coverage -Isrc/main -DMOCKING $(CFLAGS) $(shell pkg-config --libs --cflags criterion) $^ -o tests
./tests
clean:
rm -rf *.gcda *.gcno *.info $(OBJ)
fclean: clean
rm -f $(NAME)
rm -rf tests
re: fclean all
Is there a solution to detect memory error in a C program?
This C analyzer can tell you that there is something wrong when your function is misused:
https://taas.trust-in-soft.com/tsnippet/t/cf1e64df
It tells you, when applied to dest and src, that an invalid write happens inside your function at str1[i] = 0;.
Valgrind's Memcheck has some limitations regarding arrays allocated on the stack. Since it works with normally compiled binaries, it can't know the exact array sizes under certain circumstances.
clang's AddressSanitizer is better in this regard. If you compile with -fsanitize=address, you'll get the following error:
=================================================================
==15908==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffdb7e06beb at pc 0x0000005286d2 bp 0x7ffdb7e06b40 sp 0x7ffdb7e06b38
WRITE of size 1 at 0x7ffdb7e06beb thread T0
#0 0x5286d1 in my_strcpy (/home/runner/.bin.tio+0x5286d1)
#1 0x52883d in main (/home/runner/.bin.tio+0x52883d)
#2 0x7ff3e3fae412 in __libc_start_main (/lib64/libc.so.6+0x24412)
#3 0x41b33d in _start (/home/runner/.bin.tio+0x41b33d)
Address 0x7ffdb7e06beb is located in stack of thread T0 at offset 43 in frame
#0 0x5286ff in main (/home/runner/.bin.tio+0x5286ff)
This frame has 1 object(s):
[32, 43) '.compoundliteral' <== Memory access at offset 43 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism, swapcontext or vfork
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow (/home/runner/.bin.tio+0x5286d1) in my_strcpy
Shadow bytes around the buggy address:
0x100036fb8d20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100036fb8d30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100036fb8d40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100036fb8d50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100036fb8d60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x100036fb8d70: 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00[03]f3 f3
0x100036fb8d80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100036fb8d90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100036fb8da0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100036fb8db0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x100036fb8dc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
Shadow gap: cc
==15908==ABORTING

Inexplicable Segmentation fault:11

I have this code below to implement maximum subsequence length searching. I couldn't check if the program is right or not since running my program produces segmentation faults.
The program compiles fine.
Please tell me what I am doing wrong here:
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int a[6]={1,-2,4,33,0,-6}; //THE ACTUAL SEQUENCE
int count_a[6]={1,1,1,1,1,1}; //ARRAY TO KEEP COUNT OF MAXIMUM LENGTHS FROM POINT OF VIEW OF EACH ELEMENT OF ARRAY A
int i=0;
int j=0;
int k=0;
for(k=1;k<6;k++)
{
j=k;
printf("k's value:%d\t",j); //JUST FOR TESTING
while(1)
{
if(a[i]<a[j])
{
if(count_a[j]<(1+count_a[i]))
{
count_a[j]=1+count_a[i];
}
}
if(j-1==1)
{
break;
}
else
{
i++;
}
}
i=0;
}
/* THIS IS FOR ME TO CHECK WHETHER THE LENGTH VALUES HAVE BEEN UPDATED IN THE COUNT_A ARRAY*/
for(k=0;k<6;k++)
{
printf("%d\t",count_a[k]);
}
return 0;
}
You are having infinite loop. j=k; then while(1) can only terminate when if(j-1==1). On the first iteration k is set to 1 so j is also set to 1. Never breaks because 1-1!=1. Your j is never changed inside while(1) loop.
i++ is always the option. When i becomes large it tries to read from outside of array count_a[i] causing segfault.
Let's use clang's address sanitizer to figure out what is going on:
[3:41pm][wlynch#watermelon /tmp] clang -g -fsanitize=address blah.c
[3:41pm][wlynch#watermelon /tmp] ./a.out
=================================================================
==22763==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff52950958 at pc 0x00010d2b0953 bp 0x7fff52950910 sp 0x7fff52950908
READ of size 4 at 0x7fff52950958 thread T0
#0 0x10d2b0952 in main blah.c:20
#1 0x7fff851205ac in start (/usr/lib/system/libdyld.dylib+0x35ac)
#2 0x0 (<unknown module>)
Address 0x7fff52950958 is located in stack of thread T0 at offset 56 in frame
#0 0x10d2b072f in main blah.c:8
This frame has 2 object(s):
[32, 56) 'a' <== Memory access at offset 56 overflows this variable
[96, 120) 'count_a'
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
(longjmp and C++ exceptions *are* supported)
SUMMARY: AddressSanitizer: stack-buffer-overflow blah.c:20 main
Shadow bytes around the buggy address:
0x1fffea52a0d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1fffea52a0e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1fffea52a0f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1fffea52a100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1fffea52a110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x1fffea52a120: 00 00 00 00 f1 f1 f1 f1 00 00 00[f2]f2 f2 f2 f2
0x1fffea52a130: 00 00 00 f3 f3 f3 f3 f3 00 00 00 00 00 00 00 00
0x1fffea52a140: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1fffea52a150: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1fffea52a160: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x1fffea52a170: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Heap right redzone: fb
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==22763==ABORTING
k's value:1 Abort
So you're accessing beyond the end of a[].
If I reduce your code down, we can see that this is obviously possible:
k = 1;
j = k;
while(1)
{
if(a[i]<a[j])
if(count_a[j]<(1+count_a[i]))
count_a[j]=1+count_a[i];
if(j-1==1)
break;
else
i++;
}
Note that j-1 will never equal 1 on this iteration (it will always be 0), so you will increment i until it is well past the end of a.

Is accessing a global array outside its bound undefined behavior?

I just had an exam in my class today --- reading C code and input, and the required answer was what will appear on the screen if the program actually runs. One of the questions declared a[4][4] as a global variable and at a point of that program, it tries to access a[27][27], so I answered something like "Accessing an array outside its bounds is an undefined behavior" but the teacher said that a[27][27] will have a value of 0.
Afterwards, I tried some code to check whether "all uninitialized golbal variable is set to 0" is true or not. Well, it seems to be true.
So now my question:
Seems like some extra memory had been cleared and reserved for the code to run. How much memory is reserved? Why does a compiler reserve more memory than it should, and what is it for?
Will a[27][27] be 0 for all environment?
Edit :
In that code, a[4][4] is the only global variable declared and there are some more local ones in main().
I tried that code again in DevC++. All of them is 0. But that is not true in VSE, in which most value are 0 but some have a random value as Vyktor has pointed out.
You were right: it is undefined behavior and you cannot count it always producing 0.
As for why you are seeing zero in this case: modern operating systems allocate memory to processes in relatively coarse-grained chunks called pages that are much larger than individual variables (at least 4KB on x86). When you have a single global variable, it will be located somewhere on a page. Assuming a is of type int[][] and ints are four bytes on your system, a[27][27] will be located about 500 bytes from the beginning of a. So as long as a is near the beginning of the page, accessing a[27][27] will be backed by actual memory and reading it won't cause a page fault / access violation.
Of course, you cannot count on this. If, for example, a is preceded by nearly 4KB of other global variables then a[27][27] will not be backed by memory and your process will crash when you try to read it.
Even if the process does not crash, you cannot count on getting the value 0. If you have a very simple program on a modern multi-user operating system that does nothing but allocate this variable and print that value, you probably will see 0. Operating systems set memory contents to some benign value (usually all zeros) when handing over memory to a process so that sensitive data from one process or user cannot leak to another.
However, there is no general guarantee that arbitrary memory you read will be zero. You could run your program on a platform where memory isn't initialized on allocation, and you would see whatever value happened to be there from its last use.
Also, if a is followed by enough other global variables that are initialized to non-zero values then accessing a[27][27] would show you whatever value happens to be there.
Accessing an array out of bounds is undefined behavior, which means the results are unpredictable so this result of a[27][27] being 0 is not reliable at all.
clang tell you this very clearly if we use -fsanitize=undefined:
runtime error: index 27 out of bounds for type 'int [4][4]'
Once you have undefined behavior the compiler can really do anything at all, we have even seen examples where gcc has turned a finite loop into an infinite loop based on optimizations around undefined behavior. Both clang and gcc in some circumstances can generate and undefined instruction opcode if it detects undefined behavior.
Why is it undefined behavior, Why is out-of-bounds pointer arithmetic undefined behaviour? provides a good summary of reasons. For example, the resulting pointer may not be a valid address, the pointer could now point outside the assigned memory pages, you could be working with memory mapped hardware instead of RAM etc...
Most likely the segment where static variables are being stored is much larger then the array you are allocating or the segment that you are stomping though just happens to be zeroed out and so you are just lucky in this case but again completely unreliable behavior. Most likely your page size is 4k and access of a[27][27] is within that bound which is probably why you are not seeing a segmentation fault.
What the standard says
The draft C99 standard tell us this is undefined behavior in section 6.5.6 Additive operators which covers pointer arithmetic which is what an array access comes down to. It says:
When an expression that has integer type is added to or subtracted
from a pointer, the result has the type of the pointer operand. If the
pointer operand points to an element of an array object, and the array
is large enough, the result points to an element offset from the
original element such that the difference of the subscripts of the
resulting and original array elements equals the integer expression.
[...]
If both the pointer operand and the result point to elements of the
same array object, or one past the last element of the array object,
the evaluation shall not produce an overflow; otherwise, the behavior
is undefined. If the result points one past the last element of the
array object, it shall not be used as the operand of a unary *
operator that is evaluated.
and the standards definition of undefined behavior tells us that the standard imposes no requirements on the behavior and notes possible behavior is unpredictable:
behavior, upon use of a nonportable or erroneous program construct or
of erroneous data, for which this International Standard imposes no
requirements
NOTE Possible undefined behavior ranges from ignoring the situation
completely with unpredictable results, [...]
Here is the quote from the standard, that specifies what is undefined behavior.
J.2 Undefined behavor
An array subscript is out of range, even if an object is apparently accessible with the
given subscript (as in the lvalue expression a[1][7] given the declaration int
a[4][5]) (6.5.6).
Addition or subtraction of a pointer into, or just beyond, an array object and an
integer type produces a result that points just beyond the array object and is used as
the operand of a unary * operator that is evaluated (6.5.6).
In your case you the array subscript is completely outside of the array. Depending that the value will be zero is completely unreliable.
Furthermore the behavior of entire program is in question.
If just run your code from visual studio 2012 and got result like this (different at each run):
Address of a: 00FB8130
Address of a[4][4]: 00FB8180
Address of a[27][27]: 00FB834C
Value of a[27][27]: 0
Address of a[1000][1000]: 00FBCF50
Value of a[1000][1000]: <<< Unhandled exception at 0x00FB3D8F in GlobalArray.exe:
0xC0000005: Access violation reading location 0x00FBCF50.
When you look at Modules window you see that your application module memory range is 00FA0000-00FBC000. And unless you have CRT Checks turned on nothing will control what do you do inside your memory (as long as you don't violate memory protection).
So you got 0 at a[27][27] purely by chance. When you open memory view from position 00FB8130 (a) you will probably see something like this:
0x00FB8130 08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00FB8140 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00FB8150 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00FB8160 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00FB8170 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00FB8180 01 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 ................
0x00FB8190 c0 90 45 00 b0 e9 45 00 00 00 00 00 00 00 00 00 À.E.°éE.........
0x00FB81A0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00FB81B0 00 00 00 00 80 5c af 0f 00 00 00 00 00 00 00 00 ....€\¯.........
0x00FB81C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
..........
0x00FB8330 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00FB8340 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ <<<<
0x00FB8350 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
.......... ^^ ^^ ^^ ^^
It's possible that with your compiler you will always get 0 for that code because of how it uses memory, but just few bytes away you can find another variable.
For example with memory shown above a[6][0] points to address 0x00FB8190 which contains integer value of 4559040.
Then get your teacher to explain this one.
I don't know if this will work on your system but playing about with blatting memory AFTER the array a with non-zero'd bytes gives a different result for a[27][27].
On my system, when I printed contents of a[27][27] it was 0xFFFFFFFF. ie -1 converted to unsigned is all bits set in twos complement.
#include <stdio.h>
#include <string.h>
#define printer(expr) { printf(#expr" = %u\n", expr); }
unsigned int d[8096];
int a[4][4]; /* assuming an int is 4 bytes, next 4 x 4 x 4 bytes will be initialised to zero */
unsigned int b[8096];
unsigned int c[8096];
int main() {
/* make sure next bytes do not contain zero'd bytes */
memset(b, -1, 8096*4);
memset(c, -1, 8096*4);
memset(d, -1, 8096*4);
/* lets check normal access */
printer(a[0][0]);
printer(a[3][3]);
/* Now we disrepect the machine - undefined behaviour shall result */
printer(a[27][27]);
return 0;
}
This is my output:
a[0][0] = 0
a[3][3] = 0
a[27][27] = 4294967295
I saw in comments about viewing memory in Visual Studio. Easiest way is to add a break-point somewhere in your code (to halt execution) then go into Debug... windows... Memory menu, select eg Memory 1. You then find the memory address of your array a. In my case address was 0x0130EFC0. so you enter 0x0130EFC0 in the address fiend and press Enter. This shows the memory at that location.
Eg in my case.
0x0130EFC0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ..................................
0x0130EFE2 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ff ff ff ff ..............................ÿÿÿÿ
0x0130F004 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
0x0130F026 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
0x0130F048 ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
The zeros are of the course the array a, which has a byte size of 4 x 4 x sizeof an int (4 in my case) = 64 bytes. The bytes from address 0x0130EFC0 are 0xFF each (from b,c, or d contents).
Note that:
0x130EFC0 + 64 = 0x130EFC0 + 0x40 = 130F000
which is that the start of all those ff bytes you see. Probably array b.
For common compilers, accessing an array beyond its bounds can give predictable results only in very special cases, and you should not rely on that. Example :
int a[4][4];
int b[4][4];
Provided there are no alignment problem, and you ask neither aggressive optimisation nor sanitization checks, a[6][1] should in reality be b[2][1]. But please never do that in production code !
On a particular system, your teacher may be correct -- that may be how your particular compiler and operating system would behave.
On a generic system (i.e. without "insider" knowledge) then your answer is correct: this is UB.
First of all C language have not boundary check. In effect it have no check at all on almost everything. This is the joy and the doom of C.
Now going back to the issue, if you overflow the memory doesn't mean that you trigger a segfault.
Lets have a closer look to how it works.
When you start a program, or enter a subroutine the processor saves on the stack the address to which return when function ends.
The stack has been initialized from OS during process memory allocation, and got a range of legal memory where you can read or write as you like, not only store return addresses.
The common practice used by compilers to create local (automatic) variables is to reserve some space on the stack, and use that space for variables. Look following well known 32 bits assembler sequence, named prologue, that you'll find on any function enter:
push ebp ;save register on the stack
mov ebp,esp ;get actual stack address
sub esp,4 ;displace the stack of 4 bytes that will be used to store a 4 chars array
considering that stack grows in the reverse direction of data, the layout of memory is:
0x0.....1C [Parameters (if any)] ;former function
0x0.....18 [Return Address]
0x0.....14 EBP
0x0.....10 0x0......x ;Local DWORD parameter
0x0.....0C [Parameters (if any)] ;our function
0x0.....08 [Return Address]
0x0.....04 EBP
0x0.....00 0, 'c', 'b', 'a' ;our string of 3 chars plus final nul
This is known as stack frame.
Now consider the string of four bytes starting at 0x0....0 and ending at 0x....3. If we write more than 3 chars in the array we will go replacing sequentially: the saved copy of EBP, the return address, parameters, local variables of previous function then its EBP, return address, etc.
The most scenographic effect we get is that, on function return, the CPU try to jump back to a wrong address generating a segfault. Same behaviour can be achieved if one of local variables are pointers, in this case we will try to read, or write, to wrong locations triggering again the segfault.
When segfault could not happen:
when the bloated variable is not on the stack, or you have so many local variables that you overwrite them without touching the return address (and they are not pointers).
Another case is that the processor reserves a guard space between local variables and return address, in this case the buffer overflow doesn't reach the address.
Another possibility is accessing array elements randomly, in this case an oversized array can exceed stack space and overflow on other data, but luckily we mdon't touch those elements that are mapped where is saved the return address (everythibng can happen...).
When we can have segfault bloating variables that are not on stack?
When overflowing array bound or pointers.
I hope these are useful info...

Assign a array of int in C but got an extra int 32767

I have the following code in C:
int main(){
int array[] = {1,2,3,3,4,5,6,7,8};
printf("%d\n", array[8]);
printf("%d\n", array[9]);
}
When I execute it, I get that the result is 8 and 32767. I'm confused why.
update
sorry about the 3,3 thing.
what really confused me is the number 32767, if its a undefined behavior why is it always 32767? And I have discovered a weired thing:
int main(){
int arrayB[] = {7,4,3,4,1};
for(int i = 0; *(arrayB+i); i++){
printf("%d\n",*(arrayB+i) );
}
}
I wrote this, and the number 32767 appears again. AND The creepy part is if I change the array to any other length (delete or add numbers), it worked just fine!!!
You're getting undefined behavior.
You can't print the 10th element of a 9-element arrray, that's not valid code.
You're only getting 8 for array[8] because you have 3 two times in the initializer list.
Your array looks like this in memory:
+---+---+---+---+---+---+---+---+---+
index: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
+---+---+---+---+---+---+---+---+---+
data: | 1 | 2 | 3 | 3 | 4 | 5 | 6 | 7 | 8 |
+---+---+---+---+---+---+---+---+---+
So, you can see that the data at array[8] is really 8, but that's the last element in the array. If you index with an index larger than 8, you get undefined behavior.
The array has 9 valid positions but you have to take into account that they are indexed in C starting by 0.
So if you want the first value: array[0] ;
if you want the last value: array[8] ;
array[9] is not a valid position.
Hope that helps.
In C, uninitialized variables get garbage values which happens in your case as array[9] is not defined because indexing in C starts with 0 not 1.
In C, array elements are counted from zero to the length minus one. If you use the GCC compiler you can add the option -fsanitize=address to detect off-by-one errors like this.
Source:
$ cat test.c
#include <stdio.h>
int main(void)
{
int array[] = {1, 2, 3, 3, 4, 5, 6, 7, 8};
printf("%d\n", array[8]);
printf("%d\n", array[9]);
return 0;
}
Compilation command:
$ gcc -ansi -g -pedantic -Wall -fsanitize=address -o test test.c
Output:
$ ./test
8
=================================================================
==4517== ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff70a43794 at pc 0x400b2b bp 0x7fff70a43740 sp 0x7fff70a43738
READ of size 4 at 0x7fff70a43794 thread T0
#0 0x400b2a (/tmp/test+0x400b2a)
#1 0x7f1ff1180ec4 (/lib/x86_64-linux-gnu/libc-2.19.so+0x21ec4)
#2 0x400788 (/tmp/test+0x400788)
Address 0x7fff70a43794 is located at offset 68 in frame <main> of T0's stack:
This frame has 1 object(s):
[32, 68) 'array'
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
(longjmp and C++ exceptions *are* supported)
Shadow bytes around the buggy address:
0x10006e1406a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10006e1406b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10006e1406c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10006e1406d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10006e1406e0: 00 00 00 00 00 00 00 00 00 00 f1 f1 f1 f1 00 00
=>0x10006e1406f0: 00 00[04]f4 f4 f4 f3 f3 f3 f3 00 00 00 00 00 00
0x10006e140700: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10006e140710: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10006e140720: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10006e140730: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10006e140740: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Heap righ redzone: fb
Freed Heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
ASan internal: fe
==4517== ABORTING
It is also a good idea to always compile with all warnings enabled.
Array index start from 0. So you should not access 10th element.
array[0] is storing 1.
array[1] is storing 2.
array[2] is storing 3.
array[3] is storing 3.
array[4] is storing 4.
array[5] is storing 5.
array[6] is storing 6.
array[7] is storing 7.
array[8] is storing 8.
When you try to access array[9] you will get undefined behavior.

Resources