Christian S Christian S - 11 months ago 40
C Question

C program won't compile without warnings when GCC is called by makefile - Works otherwise

So I have a program here, that works perfectly when called with

$ gcc test.c -o test -std=c99

But when called in a makefile:

all: test

test: test.c
gcc test.c -o test -std=c99

it produces some warnings instead and gives a segmentation fault.

terminal output:

gcc -g test.c -o tester -std=c99
test.c: In function ‘test_split’:
test.c:43:2: warning: implicit declaration of function‘strdup[-Wimplicit-function-declaration]
char *str_cpy = strdup(str); // Allow mutation of original string
test.c:43:18: warning: initialization makes pointer from integer without a cast [enabled by default]
char *str_cpy = strdup(str); // Allow mutation of original string

Above error does not appear otherwise and does not produse a segmentation fault.

The code segment that fails is here. string.h is included in header.
The file is just a large file to test other functions.

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

#define CNRM "\x1b[0m"
#define CRED "\x1b[31m"
#define CGRN "\x1b[32m"

int stringsum(char *s);
void stringsum2(char *s, int *res);
int distance_between(char *s, char c);
char *string_between(char *s, char c);
char **split(char *s);

static int test_num = 1;

static void logger(int passed, char *s)
char *res;
char *color;

if (passed) {
res = "PASS";
color = CGRN;
} else {
res = "FAIL";
color = CRED;
printf("[Test %d][%s%s%s] %s\n", test_num++, color, res, CNRM, s);

static void test_split(char *str, char **correct)
int i, pass = 1;
char buf[512] = { 0 };
char *str_cpy = strdup(str); // Allow mutation of original string
char **res = split(str_cpy);

if (!res || !res[0]) {
pass = 0;
sprintf(buf, "split() returned NULL or an empty array");
goto end;

for (i = 0; correct[i]; i++) {
if (!res[i]) {
pass = 0;
sprintf(buf, "split() returned fewer words than expected");
goto end;

if (res[i]) {
pass = 0;
sprintf(buf, "split() returned more words than expected");
goto end;

sprintf(buf, "\n%-16s%-16s\n", "Returned", "Expected");

for (i = 0; res[i]; i++) {
char tmp[256] = { 0 };
sprintf(tmp, "%-16s%-16s\n", res[i], correct[i]);
strcat(buf, tmp);
if (strcmp(res[i], correct[i])) {
pass = 0;
goto end;

logger(pass, buf);

static void test_stringsum(char *input, int expected)
int test;
char buf[256] = { 0 };

test = stringsum(input);
sprintf(buf, "Returned: %d, Expected: %d", test, expected);
logger(test == expected, buf);

static void test_distance_between(char *str, char c, int expected)
int test;
char buf[256] = { 0 };

test = distance_between(str, c);
sprintf(buf, "Returned: %d, Expected: %d", test, expected);
logger(test == expected, buf);

static void test_string_between(char *str, char c, const char *expected)
char *res_char;
char buf[256] = { 0 };

res_char = string_between(str, c);
snprintf(buf, sizeof(buf), "Returned: %s, Expected: %s", res_char, expected);

if (!res_char && expected) {
logger(0, buf);
} else {
if (!expected)
logger(!res_char, buf);
logger(!strcmp(res_char, expected), buf);

static void test_stringsum2(char *input, int expected)
int res_int;
char buf[256] = { 0 };

stringsum2(input, &res_int);
sprintf(buf, "Returned: %d, Expected: %d", res_int, expected);
logger(res_int == expected, buf);

int main(void)
printf("Testing stringsum()\n");
test_stringsum("abcd", 10);
test_stringsum("a!", -1);
test_stringsum("aAzZ", 54);
test_stringsum("ababcDcabcddAbcDaBcabcABCddabCddabcabcddABCabcDd", 120);
test_stringsum("", 0);

test_num = 1;
printf("\nTesting distance_between()\n");
test_distance_between("a1234a", 'a', 5);
test_distance_between("a1234", 'a', -1);
test_distance_between("123456a12334a123a", 'a', 6);
test_distance_between("", 'a', -1);

test_num = 1;
printf("\nTesting string_between()\n");
test_string_between("a1234a", 'a', "1234");
test_string_between("a1234", 'a', NULL);
test_string_between("A123adette er svaretaasd2qd3asd12", 'a', "dette er sv");
test_string_between("", 'a', NULL);

test_num = 1;
printf("\nTesting stringsum2()\n");
test_stringsum2("abcd", 10);
test_stringsum2("abcd!", -1);
test_stringsum2("bbbdbbbbbdbbdbbbbbddbbbbbdbbdbbbbdbd", 90);
test_stringsum2("", 0);

test_num = 1;
printf("\nTesting split()\n");
test_split("abcd", (char *[]){ "abcd", NULL });
test_split("Hei du", (char *[]){ "Hei", "du", NULL });
test_split("Dette er mange ord", (char *[]){ "Dette", "er", "mange", "ord", NULL });
return 0;

Any ideas?

Edit: Added full code.

Answer Source

strdup() is defined a bunch of standards (SVr4, 4.3BSD, POSIX.1-2001), but not the C standard. If you specify -std=c99, gcc by default disables functions defined in these standards. The warning about "implicit declaration" is because C has (annoying) legacy feature of declaring functions implicitly, if you just call them, with certain rules such as return type int, which in this case doesn't match your assignment to char*. These warnings are something you should always fix.

In this case, with -std=c99, it can be fixed by using feature test macros.

The linked manual page tells in which cases the function declaration will be included, so for example this will build it without problems:

gcc test.c -o test -std=c99 -D_POSIX_C_SOURCE=200809L

or with gcc you can use this, which enables pretty much all the features of the libc.

gcc test.c -o test -std=c99 -D_GNU_SOURCE

You could also add the define in the .c file as normal #define before you include the relevant system headers. It may be better to have it at the same place as the -std=c99 though (ie. command line option), because here it kinda goes together with that.

A more extreme solution would be to switch to -std=gnu99. But think carefully about doing that, because then it is easy to accidentally use language features which aren't standard C. And then you will run into much more porting trouble, than just writing a few functions, if you need to port the software to other C compiler which doesn't have GNU extensions.