Issue
I have a little C program where a couple of functions should print the current date, but they don't always do:
#include <stdio.h>
#include <time.h>
#include <string.h>
#define BUF_LEN 16
typedef struct {
const char *year;
const char *month;
const char *day;
} date_s;
date_s string_to_date(char *buffer) {
if (buffer) {
const char *year = strtok(buffer, "/");
const char *month = strtok(NULL, "/");
const char *day = strtok(NULL, "/");
printf("INFO 2: YEAR: %s, MONTH: %s, DAY: %s\n", year, month, day);
return (date_s) {.year = year, .month = month, .day = day};
} else {
return (date_s) {.year = "0000", .month = "00", .day = "00"};
}
}
date_s get_current_date(void) {
char buffer[BUF_LEN] = {0};
time_t t = time(NULL);
const struct tm *tm = localtime(&t);
strftime(buffer, BUF_LEN, "%Y/%m/%d", tm);
printf("INFO 1: %s\n", buffer);
return string_to_date(buffer);
}
void print_data1(const char *year, const char *month, const char *day) {
printf("1.) YEAR: %s, MONTH: %s, DAY: %s\n", year, month, day);
}
void print_data2(date_s date) {
printf("2.) YEAR: %s, MONTH: %s, DAY: %s\n",
date.year,
date.month,
date.day);
}
int main(void) {
date_s current_date = get_current_date();
// This prints always the expected values.
//date_s current_date = {.year = "0000", .month = "00", .day = "00"};
printf("INFO 3: YEAR: %s, MONTH: %s, DAY: %s\n",
current_date.year,
current_date.month,
current_date.day);
print_data2(current_date);
print_data1(current_date.year, current_date.month, current_date.day);
return 0;
}
Compiling it like this does not show the expected result:
gcc -g -Wall -O0 -std=c99 -o example example.c
gcc -g -Wall -O1 -std=c99 -o example example.c
It shows something like this:
INFO 1: 2020/07/13
INFO 2: YEAR: 2020, MONTH: 07, DAY: 13
INFO 3: YEAR: 2020, MONTH: 07, DAY: 13
2.) YEAR: �8���, MONTH: , DAY: [����U
1.) YEAR: �8���, MONTH: , DAY: X8���
Compiling it like this does show the expected result:
gcc -g -Wall -O2 -std=c99 -o example example.c
gcc -g -Wall -O3 -std=c99 -o example example.c
Result:
INFO 1: 2020/07/13
INFO 2: YEAR: 2020, MONTH: 07, DAY: 13
INFO 3: YEAR: 2020, MONTH: 07, DAY: 13
2.) YEAR: 2020, MONTH: 07, DAY: 13
1.) YEAR: 2020, MONTH: 07, DAY: 13
Questions:
- Why is the code not showing the current date in the first two cases? (-O0 and -O1)
- Why is the code working in the last two cases? (-O2 and -O3)
- What can I do to make it always work? Also independently of gcc optimization level.
Solution
The problem is here:
return (date_s) {.year = year, .month = month, .day = day};
Your program has undefined behavior because the structure date_s
returned by string_to_date
is initialized with pointers that point to an array with automatic storage that has gone out of scope by the time the pointers are used in main
to print the date.
Depending on the compiler options, the contents of the array may or may not be modified by subsequent code executed. Undefined behavior can cause a number of side effects, including no visible effect.
You could fix this problem by allocating copies of the strings, which would need to be freed when the structure is no longer used:
#include <stdio.h>
#include <time.h>
#include <string.h>
#define BUF_LEN 16
typedef struct {
char *year;
char *month;
char *day;
} date_s;
date_s string_to_date(const char *buffer) {
if (buffer) {
const char *year = buffer;
int year_length = strcspn(year, "/");
const char *month = year + year_length + (year[year_length] == '/');
int month_length = strcspn(month, "/");
const char *day = month + month_length + (month[month_length] == '/');
printf("INFO 2: YEAR: %.*s, MONTH: %.*s, DAY: %s\n",
year_length, year, month_length, month, day);
return (date_s) {
.year = strndup(year, year_length),
.month = strndup(month, month_length),
.day = strdup(day)
};
} else {
return (date_s) {
.year = strup("0000"), .month = strdup("00"), .day = strdup("00")
};
}
}
date_s get_current_date(void) {
char buffer[BUF_LEN] = { 0 };
time_t t = time(NULL);
const struct tm *tm = localtime(&t);
strftime(buffer, BUF_LEN, "%Y/%m/%d", tm);
printf("INFO 1: %s\n", buffer);
return string_to_date(buffer);
}
void print_data1(const char *year, const char *month, const char *day) {
printf("1.) YEAR: %s, MONTH: %s, DAY: %s\n", year, month, day);
}
void print_data2(date_s date) {
printf("2.) YEAR: %s, MONTH: %s, DAY: %s\n",
date.year,
date.month,
date.day);
}
int main(void) {
date_s current_date = get_current_date();
// This prints always the expected values.
//date_s current_date = {.year = "0000", .month = "00", .day = "00"};
printf("INFO 3: YEAR: %s, MONTH: %s, DAY: %s\n",
current_date.year,
current_date.month,
current_date.day);
print_data2(current_date);
print_data1(current_date.year, current_date.month, current_date.day);
free(current_date.year);
free(current_date.month);
free(current_date.day);
return 0;
}
Answered By - chqrlie