Home > Mobile >  Unexpected behaviors with fixed-size strings
Unexpected behaviors with fixed-size strings

Time:07-27

I have recently started using structs in C , and naturally, I started applying them with a simple Person struct that stores values such as name, telephone number, adress, etc.

As I was doing so, I thought of leaving the phone and id variables to store a maximum number of digits, I set that size to be 10 for both, and I did so by initializing them as char arrays instead of integers, since that lets me keep track of their size easier:

#include <iostream>

struct Person
{
   /*For the sake of the example I'll only be working with these since are the ones giving me trouble*/
   char id[10];
   char phone[10];
};

The problem started when I executed the following:

int main()
{
    Person gary;
    std::cout << "Type your id: ";
    std::cin >> gary.id;
    std::cout << "Type your phone number: ";
    std::cin >> gary.phone;
    std::cout << "\nYour id is : " << gary.id <<
        "Your phone number is: " << gary.phone;

    return 0;
}

Which produces the following (including my input)

Type your id: 0123456789 //Here I typed numbers from 0 to 9, fullfilling the expected maximum size of 10
Type your phone number: 9876543210 //More of the same, but backwards so is identifiable

Your id is : 01234567899876543210 //Why? Somehow it saved both it's input and the one in gary.phone
Your phone number is: 9876543210 //Fine, at least is storing what is supposed to store.

As you can tell, for some reason, the gary.id variable is not only storing it's supposed input, but also the one that belongs to gary.phone, not to mention the fact that is storing more than 10 digits, contrary to what the code suggests.

At first I thought it had something to do with the structs, and despite I checked multiple times and concluded that there was nothing wrong with my struct, I decided to remove it to know if the issue was actually there, ending up with this code:

#include <iostream>

int main()
{
    char id[10];
    char phone[10];
    std::cout << "Type your id: ";
    std::cin >> id;
    std::cout << "Type your phone number: ";
    std::cin >> phone;
    std::cout << "\nYour id is : " << id <<
        "\nYour phone number is: " << phone;

    return 0;
}

The core is the same, and when executed, it looks basically the same, so with the same input, one would expect the same output, right?

Type your id: 0123456789 //same
Type your phone number: 9876543210 //same

Your id is : //????
Your phone number is: 9876543210 //same

In this run, the program now doesn't even store the input for id, whereas before it stored everything next to it, why is this?

I've worked around the problem testing different "solutions", and getting short of ideas I started changing the order of the cin, maybe the initialization, everything, but the error either repeats or something else happens, unexpected as well.

Anyone knows why does this happen? How can I work around it? I know about the existance of std::string (in the library), and I actually use it quite often, but I don't know of a way to restrict it's character storage.

CodePudding user response:

You entered 10 characters, so there is no room left in the char array for the terminating null character ('\0'). Thus, cout does not know when to stop printing the string and will probably go past the end of the memory allocated for that array.

Note that this issue is fixed in C 20, as istreams will only read into char arrays, not char pointers, and read at most up to one less than the array's size.

It is generally recommended to use std::string from the <string> header instead as it is simpler and more powerful.

CodePudding user response:

C style strings need an extra char to store the nul terminator ('\0').

So the solution to your problem is to add one to the size of your arrays

struct Person
{
   char id[11];
   char phone[11];
};

This is true irrespective of whether the array is in a struct or not.

But this code is dangerous, what if the user types in more than 10 characters. For this reason (and others) you should use a string than can be any length. C has just such a thing, it's called std::string

#include <string>

struct Person
{
   std::string id;
   std::string phone;
};

CodePudding user response:

The problem is how C and other low level languages identify the end of an array. Your size of 10, says you can store ten characters, however, that does not leave room for the \0 to indicate the array's end. Structs store one record after another, so your id, because it does not have an end character, runs directly into the phone, so asking it to print id grabs everything. Your request to print phone, just starts as expected.

Make it 11, and try your 10 digit numbers. The problem should be resolved.

The way to prevent this is to use a buffer of some longer length to capture your input. You can either trust the data and only put 10 characters into the id, or you could loop back and demand correct input.

Note: You are actually lucky that both of the arrays did not continue printing garbage data until they reached a hex value of 00! I expect that the memory allocation pre-formatted the space with all zeroes for you, but if you were to do this multiple times and add some other data, that you would be seeing your 20 characters and a ton of text that doesn't mean anything to your application.

  • Related