0

I have the code below. When I print out the data I assign to the character arrays in the struct it prints out a bunch of junk unless I add an extra character to the array and put "/0". The problem with this is later on I need to grab the birth day and month as an integer (4 bytes). Can anyone tell me how to make it print out the appropriate data? I need the char array to be the exact size in memory of the size of the data.

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

struct Info
{
        char studentID[6];
        char firstName[5];
        char lastName[4];
        char birthDay[2];
        char birthMonth[2];
        char birthYear[4];
};

void printFunction( struct Info studentInfo );

int main()
{
        struct Info studentInfo = {"999999", "first", "last", "01", "07", "1993"};
        void *baseptr;
        asm("movl %%ebp,%0;":"=r"(baseptr));
        printf("The value of the basepointer main:\n");
        printf("%p\n", baseptr);
        printf("%-15s %s \n", "Student ID:", studentInfo.studentID);
        printf("%-15s %s \n", "First Name:", studentInfo.firstName);
        printf("%-15s %s \n", "Last Name:", studentInfo.lastName);
        printf("%-15s %s \n", "Birth Day:", studentInfo.birthDay);
        printf("%-15s %s \n", "Birth Month:", studentInfo.birthMonth);
        printf("%-15s %s \n", "Birth Year:", studentInfo.birthYear);

        printFunction( studentInfo );
        return 0;
}

void printFunction( struct Info studentInfo )
{
        printf("The value of basepointer printFunction is:\n");
        int *baseptr;
        asm("movl %%ebp,%0;":"=r"(baseptr));
        printf("%p\n", baseptr);
        printf("The value at basepointer address is:\n");
        printf("%p\n", *baseptr);
        printf("%-25s %p \n", "Address of Student ID:", &studentInfo.studentID);
        printf("%-25s %p \n", "Address of First Name:", &studentInfo.firstName);
        printf("%-25s %p \n", "Address of Last Name:", &studentInfo.lastName);
        printf("%-25s %p \n", "Address of Birth Day:", &studentInfo.birthDay);
        printf("%-25s %p \n", "Address of Birth Month:", &studentInfo.birthMonth);
        printf("%-25s %p \n", "Address of Birth Year:", &studentInfo.birthYear);
        printf("%s %x \n", "The address of my birth day and month is at address: ", &studentInfo.birthDay );
        printf("%s \n", "The integer value of my birth day and month is: ");
}
rkhb
  • 14,159
  • 7
  • 32
  • 60
Kevin Zaki
  • 146
  • 1
  • 2
  • 9
  • `NUL` terminator. `NUL` terminator. `NUL` terminator. `NUL` terminator. `NUL` terminator. – The Paramagnetic Croissant Dec 08 '14 at 20:57
  • possible duplicate of [null terminating a string](http://stackoverflow.com/questions/2911089/null-terminating-a-string) – The Paramagnetic Croissant Dec 08 '14 at 20:58
  • 1
    Your arrays are not big enough. You need 1 more to store the zero terminator. – Hans Passant Dec 08 '14 at 20:59
  • Shouldn't `&studentInfo.studentID` be `&studentInfo->studentID` or `studentInfo.studentID`? – Unn Dec 08 '14 at 21:03
  • here is an example fix: printf("%-15s %s \n", "Student ID:", studentInfo.studentID); should be: printf("%-15s %6s \n", "Student ID:", studentInfo.studentID); where the '6' is the number of characters to print. – user3629249 Dec 08 '14 at 22:14
  • this line: struct Info studentInfo = {"999999", "first", "last", "01", "07", "1993"} will corrupt certain fields, for instance the first field, because the compiler will produce code to copy strings and strings always have a terminating '\0' so the first literal is actually 7 characters long. – user3629249 Dec 08 '14 at 22:15
  • the code can obtain the numeric value of the strings by (when the string is null terminated or has a trailing non numeric character) value = atoi(studentInfo.studentID); – user3629249 Dec 08 '14 at 22:17
  • @Umm no , the original is fine (apart from failure to cast to `(void *)`) – M.M Dec 08 '14 at 22:29
  • @user3629249 it will not corrupt fields, a brace-enclosed initializer can never "overflow" like that. What happens is that the null terminator does not get copied. There is a special case for initializing a char array from a string literal; in other cases where you provide excess initializers it must generate a diagnostic. – M.M Dec 08 '14 at 22:31
  • @MattMcNabb OH, youre right; I thought he was trying to print the actual value of the struct field (the char array), not the address of each (didnt see the %p, assumed it was a %s). Now it makes sense :) – Unn Dec 08 '14 at 23:48

4 Answers4

2

Change your struct definition with sizes sufficient to accommodate the C string termination character: \0.

struct Info
{
        char studentID[7];
        char firstName[50];
        char lastName[50];
        char birthDay[3];
        char birthMonth[3];
        char birthYear[5];
};

Sizes shown are arbitrary in length, but for illustration of your use case, sufficient.

In memory, the member: studentID[6] is only created with enough space contain 6 characters, i.e. "999999", but not the all important string termination character: \0. C does not actually have a _string typeP. Instead, in C, a string is defined as a character array terminated by a '\0'

So, studentID[7] would look like this:

|'9'|'9'|'9'|'9'|'9'|'9'|0|  

An important point, when you create a string variable (or a char array) such as:

char string[10]  = {0};//space for 10 char

then fill it with something like:

strcpy(string, "nine char"); //9 spaces used  

The null terminator is appended as a function of calling strcpy(), giving you:

|'n'|'i'|'n'|'e'|' '|'c'|'h'|'a'|'r'|0| //9 characters AND a NULL terminator
ryyker
  • 22,849
  • 3
  • 43
  • 87
  • Suggest reserving "NULL" for the null pointer `NULL`. Use `'\0'` or "null character" here. – chux - Reinstate Monica Dec 08 '14 at 21:07
  • Thanks for the reply but actually user i486 actually gave me what I needed. I said in the post that I know you can put /0 but I can't do that for this because I need to use the memory locations in certain ways. – Kevin Zaki Dec 08 '14 at 21:15
  • @KevinZaki - Your mistake is creating memory insufficient for the data you are later attempting to assign. i.e. `"999999"` will not fit into an array of size 6. You need 7 `char` spaces to define a string with 6 useable characters and a '\0'. – ryyker Dec 08 '14 at 21:19
  • @ryyker thanks. I understand that but this is a school assignment and the next part of the assignment I can't have char[3] for because I need to pull 4 bytes from memory as an integer which would be char[2] char[2] for birhtMonth and birthDay. I understand this may not be proper coding but it is what I need to accomplish this assignment. – Kevin Zaki Dec 08 '14 at 21:24
1

In your structure

struct Info
{
        char studentID[6];
        char firstName[5];
        char lastName[4];
        char birthDay[2];
        char birthMonth[2];
        char birthYear[4];
};

variables are not having enough memory to store the null terminator. So, printing as string is wrong.

To avoid, always allow one more char to store the null terminator. With the same set of initializer,

struct Info
{
        char studentID[7];
        char firstName[6];
        char lastName[5];
        char birthDay[3];
        char birthMonth[3];
        char birthYear[5];
};

should work just fine.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
0

Maybe you want this:

printf("Birth Day: %c%c \n", studentInfo.birthDay[0], studentInfo.birthDay[1]);
i486
  • 6,491
  • 4
  • 24
  • 41
  • `struct Info studentInfo = {"99999", "firs", "las", {'0','1'}, {'0','7'}, {'1','9','9','3'} };` – i486 Dec 08 '14 at 21:01
  • This is actually what I was looking for. – Kevin Zaki Dec 08 '14 at 21:14
  • actually, losing the last chard of each text is NOT a good solution. Rather make the initializer similar to: struct Info studentInfo = {('9','9','9','9','9','9'), etc }; However, the printf() statements for the values will also need to number of char to print, as in: printf("%-15s %6s \n", "Student ID:", studentInfo.studentID); – user3629249 Dec 08 '14 at 22:26
  • I removed last char for first/last name because the other option was to change the structure. Actually, `char lastName[4];` must be modified to more realistic value - e.g. 20-30 chars instead of 4. – i486 Dec 09 '14 at 08:22
0

If you want to print out the character values in this struct, and you don't want to change the struct to contain null-terminated strings (as you suggest in comments), then you have to tell printf a limit on how many characters to print.

Normally %s prints until it encounters a null terminator, but your struct does not contain any null terminators, so you have to specify the maximum field width:

 printf("%-15s %.6s \n", "Student ID:", studentInfo.studentID);
 //             ^^

You can find this value programmatically instead of relying on magic numbers:

 printf("%-15s %.*s \n", "Student ID:", 
        (int)(sizeof studentInfo.studentID), studentInfo.studentID);

Optionally you could wrap all these lines up in a macro:

#define PRINT_ITEM(prompt, field) \
    printf("%-15s %.*s\n", prompt, (int)sizeof(field), field)

PRINT_ITEM("Student ID:", studentInfo.studentID);
PRINT_ITEM("First Name:", studentInfo.firstName);
PRINT_ITEM("Last Name:" , studentInfo.lastName);
// etc.

NB. The initializers for this struct are correct: in C it is permitted to initialize a char array from a string literal containing exactly as many non-null characters as the size of the array; and that fills the char array with those characters. (This is not permitted in C++).

M.M
  • 138,810
  • 21
  • 208
  • 365