Unexpected Behaviors

You keep using that code. I do not think it means what you think it means.

Agenda

Unspecified Behavior

behavior, that results from the use of an unspecified value, or other behavior upon which this document provides two or more possibilities and imposes no further requirements on which is chosen in any instance
ISO/IEC 9899, § 3.4.4

The implementation has to do the right thing.

For some definition of the right thing.

The implementation's definition of the right thing may or may not agree with yours.

YMMV.

Implementation-Defined Behavior

unspecified behavior where each implementation documents how the choice is made
ISO/IEC 9899:2018, § 3.4.1

The implementation has to do the right thing.

For some definition of the right thing.

And write it down.

Locale-Specific Behavior

behavior that depends on local conventions of nationality, culture, and language that each implementation documents
ISO/IEC 9899:2018, § 3.4.2

The implementation has to do the right thing.

For some definition of the right thing.

The right thing may be different in China.

Undefined Behavior

behavior, upon use of a nonportable or erroneous program construct or of erroneous data, for which this document imposes no requirements
ISO/IEC 9899:2018, § 3.4.3

There is no right thing.

Permissible undefined behavior ranges from ignoring the situation completely with unpredictable results, to having demons fly out of your nose.
John F. Woods, <10195@ksr.com> in comp.std.c

Other Oddities

The compiler knows C better than you.

A lot better.

Sometimes too much better.

This can lead to surprising results.

Examples

Caveat: As the purpose of the following examples is to demonstrate behavior that the beginner to intermediate C programmer may not expect, the code presented is not written to normal quality standards. Do not use any of the following examples as a basis for real-world code.

Here there be dragons.

And/or nasal demons.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Point of No Return

#include <stdio.h>

int foo(int i)
{
	int n = i * 42;
}

int main(void)
{
	printf("%d\n", foo(42));
}
$ gcc -o missing-return-statement missing-return-statement.c
$ ./missing-return-statement
1764
$ clang -o missing-return-statement missing-return-statement.c
missing-return-statement.c:6:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
1 warning generated.
$ ./missing-return-statement
0
$ gcc -Wall -Wextra -o missing-return-statement missing-return-statement.c
missing-return-statement.c: In function 'foo':
missing-return-statement.c:5:6: warning: unused variable 'n' [-Wunused-variable]
  int n = i * 42;
      ^
missing-return-statement.c:6:1: warning: control reaches end of non-void function [-Wreturn-type]
 }
 ^

No Return

int main(void)
{
}
$ gcc -std=c89 -Wall -Wextra -o no-return no-return.c
no-return.c: In function 'main':
no-return.c:3:1: warning: control reaches end of non-void function [-Wreturn-type]
 }
 ^
$ ./no-return ; echo $?
37
$ gcc -std=c99 -Wall -Wextra -o no-return no-return.c
$ ./no-return ; echo $?
0

Return of No Point(er)

#include <stdio.h>

int *a(void)
{
	int n = 42;
	return &n;
}

int main(void)
{
	int *ptr = a();
	printf("%d\n", *ptr);
}
$ gcc -Wall -Wextra -o lifetime lifetime.c
lifetime.c: In function 'a':
lifetime.c:6:9: warning: function returns address of local variable [-Wreturn-local-addr]
  return &n;
         ^~
$ ./lifetime
Segmentation fault

Character Classifier

#include <ctype.h>
#include <locale.h>
#include <stdio.h>

int main(void)
{
	setlocale(LC_ALL, "");
	if (ispunct('')) {
		puts("punctuation");
	} else {
		puts("not punctuation");
	}
}
$ gcc -Wall -Wextra -o punctuation punctuation.c
$ ./punctuation
not punctuation
$ LC_ALL=zh_CN.gbk ./punctuation 
punctuation

Putting the Cart Before the Horse

#include <locale.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int compare(const void *s1, const void *s2)
{
	return strcoll(*(char *const *)s1, *(char *const *)s2);
}

int main(void)
{
	char *words[] = { "cart", "Horse" };
	setlocale(LC_ALL, "");

	qsort(words, sizeof(words) / sizeof(words[0]), sizeof(words[0]), compare);

	for (size_t i = 0; i < sizeof(words) / sizeof(words[0]); i++) {
		puts(words[i]);
	}
}
$ gcc -Wall -Wextra -o string-sort string-sort.c
$ LC_ALL=C ./string-sort
Horse
cart
$ LC_ALL=en_US ./string-sort
cart
Horse

How long is a Type?

#include <stdio.h>

int main(void)
{
	printf("char:  %d\n", (int)sizeof(char));
	printf("short: %d\n", (int)sizeof(short));
	printf("int:   %d\n", (int)sizeof(int));
	printf("long:  %d\n", (int)sizeof(long));
}
$ gcc -Wall -Wextra -o type-sizes type-sizes.c
$ ./type-sizes
char:  1
short: 2
int:   4
long:  8
$ i686-linux-gnu-gcc -Wall -Wextra -o type-sizes type-sizes.c
$ ./type-sizes
char:  1
short: 2
int:   4
long:  4
$ x86_64-w64-mingw32-gcc -Wall -Wextra -o type-sizes.exe type-sizes.c
$ wine ./type-sizes.exe
char:  1
short: 2
int:   4
long:  4

Order, Please

#include <stdio.h>

int this(void)
{
	puts("This");
	return 1;
}

int is(void)
{
	puts("is");
	return 2;
}

int odd(void)
{
	puts("odd");
	return 3;
}

int main(void)
{
	printf("%d %d %d\n", this(), is(), odd());
}
$ gcc -Wall -Wextra -o order-of-evaluation order-of-evaluation.c
$ ./order-of-evaluation
odd
is
This
1 2 3
$ clang -Wall -Wextra -o order-of-evaluation order-of-evaluation.c
$ ./order-of-evaluation
This
is
odd
1 2 3

Never a NULL Moment

#include <stdio.h>

struct s {
	int i;
};

int null_check(struct s *sp)
{
	int n = sp->i;
	if (sp != NULL) {
		puts("good pointer");
	} else {
		puts("NULL pointer");
	}
	return n;
}

int main(void)
{
	struct s ss = { 0 };
	null_check(&ss);
	null_check(NULL);
}
$ gcc -Wall -Wextra -o null-pointer-dereference null-pointer-dereference.c
$ ./null-pointer-dereference
Segmentation fault
$ gcc -O3 -Wall -Wextra -o null-pointer-dereference null-pointer-dereference.c
$ ./null-pointer-dereference
good pointer
good pointer

Secure Free

#include <stdlib.h>
#include <string.h>

void secure_free(void *ptr, size_t n)
{
	memset(ptr, 0, n);
	free(ptr);
}
$ gcc -S -O3 -Wall -Wextra secure-free.c
$ grep -Ef asm.grep secure-free.s
secure_free:
	jmp	free@PLT

Twin Strings

#include <stdio.h>

#define A_STRING "a string"

int main(void)
{
	printf("s1: %p\n", (void*)A_STRING);
	printf("s2: %p\n", (void*)A_STRING);
}
$ gcc -Wall -Wextra -o identical-string-literals identical-string-literals.c
$ ./identical-string-literals
s1: 0x55f06c7aa004
s2: 0x55f06c7aa004
$ tcc -Wall -Wextra -o identical-string-literals identical-string-literals.c
$ ./identical-string-literals
s1: 0x600554
s2: 0x600565

Get inline

#include <stdio.h>

static inline int hello(void)
{
	return puts("hello, world");
}

int main(void)
{
	hello();
}
$ gcc -Wall -Wextra -S -masm=intel inline.c
$ grep -Ef asm.grep inline.s
.LC0:
	.string	"hello, world"
hello:
	push	rbp
	mov	rbp, rsp
	lea	rdi, .LC0[rip]
	call	puts@PLT
	pop	rbp
	ret
main:
	push	rbp
	mov	rbp, rsp
	call	hello
	mov	eax, 0
	pop	rbp
	ret
$ gcc -O3 -Wall -Wextra -S -masm=intel inline.c
$ grep -Ef asm.grep inline.s
.LC0:
	.string	"hello, world"
main:
	sub	rsp, 8
	lea	rdi, .LC0[rip]
	call	puts@PLT
	xor	eax, eax
	add	rsp, 8
	ret

Free Real Estate

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	char *ptr = malloc(32);
	sprintf(ptr, "hello, world");
	printf("%s\n", ptr);
	free(ptr);
	printf("%s\n", ptr);

	ptr = malloc(65536);
	sprintf(ptr, "hello, again");
	printf("%s\n", ptr);
	free(ptr);
	printf("%s\n", ptr);

	printf("goodbye\n");
}
$ gcc -Wall -Wextra -o use-after-free use-after-free.c
$ ./use-after-free
hello, world

hello, again
hello, again
goodbye

Really Free

#include <stdlib.h>

int main(void)
{
	void *ptr = malloc(16);
	free(ptr);
	free(ptr);
}
$ gcc -Wall -Wextra -o double-free double-free.c
$ ./double-free
free(): double free detected in tcache 2
Aborted

Custom Logger

#include <stdio.h>

double log(double n)
{
	printf("LOG: %g\n", n);
	return n;
}

int main(void)
{
	printf("%g\n", log(100.0));
}
$ gcc -Wall -Wextra -o reserved reserved.c
$ ./reserved
4.60517

We'll Change This Later

#include <string.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
	char *s = "placeholder for text to be copied later";
	strcpy(s, argv[argc - 1]);
	printf("%s\n", s);
}
$ gcc -Wall -Wextra -o modify-string-literal modify-string-literal.c
$ ./modify-string-literal
Segmentation fault
$ tcc -Wall -Wextra -o modify-string-literal modify-string-literal.c
$ ./modify-string-literal
./modify-string-literal

What??!

#include <stdio.h>

int main(void)
{
	puts("What the heck??!");
}
$ gcc -std=c99 -Wall -Wextra -o trigraphs trigraphs.c
trigraphs.c: In function 'main':
trigraphs.c:5:21: warning: trigraph ??! converted to | [-Wtrigraphs]
  puts("What the heck??!");
                      
$ ./trigraphs
What the heck|

Comment Your Code

#include <stdio.h>

int main(void)
{
	//// Outputs Hello \\\\
	puts("Hello");
	/*** Outputs Goodbye ***/
	puts("Goodbye");
}
$ gcc -Wall -Wextra -o comments comments.c
comments.c: In function 'main':
comments.c:5:2: warning: multi-line comment [-Wcomment]
  //// Outputs Hello \\\\
  ^
$ ./comments
Goodbye

Let's Pack It In

#pragma pack(1)
struct s {
	int i;
	char c;
};
$ gcc-1.17 pragma.c
cpp: You are in a maze of twisty compiler features, all different
$ cat gcc-pragma.c
/*
 * the behavior of the #pragma directive is implementation defined.
 * this implementation defines it as follows.
 */
do_pragma ()
{
  close (0);
  if (open ("/dev/tty", O_RDONLY) != 0)
    goto nope;
  close (1);
  if (open ("/dev/tty", O_WRONLY) != 1)
    goto nope;
  execl ("/usr/games/hack", "#pragma", 0);
  execl ("/usr/games/rogue", "#pragma", 0);
  execl ("/usr/new/emacs", "-f", "hanoi", "9", "-kill", 0);
  execl ("/usr/local/emacs", "-f", "hanoi", "9", "-kill", 0);
nope:
  fatal ("You are in a maze of twisty compiler features, all different");
}

Resources

Copyright © 2019 Jakob Kaivo <jakob@kaivo.net>