简体   繁体   中英

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;
    }
}

end:
logger(pass, buf);
free(str_cpy);
}

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);
    else
        logger(!strcmp(res_char, expected), buf);
    free(res_char);
}
}

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.

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.

If you ask gcc for C99 compliant code, it will do as you asked and forget it ever knew about strdup() - This function is not covered by the C99 standard, but is rather POSIX only, so strdup is nowhere in a header file in C99 mode.

As your code doesn't see the prototype for that reason, and doesn't know strdup () will return a pointer but rather assumes an integer, it might do all sorts of havoc when 32-/64- mixed mode code where (sizeof(int) != sizeof(char*)) is generated.

Simple remedy: Don't try to use strdup in c99 mode.

Why you seem to see the bug when compiling using make and not otherwise, is something we cannot deduct from the information you have provided. I guess for some reason you aren't switching gcc into c99 mode when using the command line.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM