Почему распечатка массива символов приводит к случайным символам?

#arrays #c #printf #c-strings

#массивы #c #printf #c-строки

Вопрос:

Я пытаюсь изучать программирование и задаю несколько случайных вопросов и задач. Задача состоит в том, чтобы записать последние три символа строки, и это то, что я придумал. Пожалуйста, не решайте это за меня, я спрашиваю о моем выводе / результате, а не о том, чтобы кто-то решал проблему за меня:

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

int func(char *input_1, char *input_2) {
    char last_three_digits_1[3]; // define two arrays that hold the last three characters
    char last_three_digits_2[3];

    int x = 0;
    for(int y = strlen(input_1) - 4; y < strlen(input_1) - 1; y  ) {
        last_three_digits_1[x] = input_1[i];
        x  ;
    }

    x = 0; // repeat for the second string
    for(int z = strlen(input_2) - 4; z < strlen(input_2) - 1; z  ) {
        last_three_digits_2[x] = input_2[i];
        x  ;
    }


    printf("last3_1: %s, nlast3_2: %sn", last_three1, last_three2); // this seem to access random memory because it outputs "abc" followed by random "-???" or "-????" or "-?2?2". Same for the second array.

    return 0;
}

int main(int argc, char * argv[] ) {
    if(argc == 3) { // needs to be two strings. E.g: "abcd efghi"
        if(strlen(argv[1]) >= 3 || strlen(argv[2]) >= 3) { // if any of the strings are less than 3 in length, e.g. "ab wadnkwa" then do not proceed.
            func(argv[1], argv[2]); // send the user input to the function
        }

        return 0; // maybe redundant
    }

    return 0; // return 0 for failure
}
 

Комментарии:

1. strlen(argv[1]) >= 3 и i = strlen(text1) - 4 — когда strlen(argv[1]) есть 3 , тогда i есть -1 .

2. вы должны начать с проверки аргументов вашей функции, например, вы предполагаете, что text1 содержит не менее 4 символов, поэтому проверьте, так ли это.

Ответ №1:

Условие в операторе if

 if(strlen(argv[1]) >= 3 || strlen(argv[2]) >= 3)
 

означает, что длина одной из строк может быть меньше 3. В этом случае функция func может вызвать неопределенное поведение.

Кажется, вы имеете в виду

 if(strlen(argv[1]) >= 3 amp;amp; strlen(argv[2]) >= 3)
 

В противном случае вам нужно в функции func проверить, имеет ли строка длину, большую или равную 3, прежде чем выводить ее последние три символа.

Если строка содержит ровно три символа, то, например, в этом цикле

 for(int i = strlen(text1) - 4; i < strlen(text1) - 1; i  )
 

переменная i может иметь значение, определенное реализацией, например, -1, и в результате это выражение text1[i] вызывает неопределенное поведение.

Возвращаемый тип int функции не имеет значения.

Обратите внимание на то, что если вы используете формат %s для вывода массива символов printf , то массив должен содержать строку, представляющую собой последовательность символов, заканчивающуюся нулевым символом '' .

Таким образом, ни оператор if в main, ни сама функция не имеют смысла.

Для вывода последних трех (или любого другого числа) символов строки нет необходимости создавать вспомогательный массив. Это можно сделать намного проще.

Например

 void func( const char *text1, const char *text2, size_t n )
{
    size_t n1 = strlen( text1 );
   
    if ( !( n1 < n ) ) text1  = n1 - n;

    size_t n2 = strlen( text2 );

    if ( !( n2 < n ) ) text2  = n2 - n;

    printf( "last%zu_1: %s, nlast%zu_2: %sn", n, text1, n, text2 );
}
 

И функция может быть вызвана как

 func( argv[1], argv[2], 3 );
 

Используя такое объявление функции, вы можете вывести любое количество последних символов двух строк. Также примите во внимание, что параметры функции имеют квалификатор const , потому что переданные строки не изменяются внутри функции.

Вот демонстрационная программа

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

void func( const char *text1, const char *text2, size_t n )
{
    size_t n1 = strlen( text1 );

    if (!( n1 < n )) text1  = n1 - n;

    size_t n2 = strlen( text2 );

    if (!( n2 < n )) text2  = n2 - n;

    printf( "last%zu_1: %s, nlast%zu_2: %sn", n, text1, n, text2 );
}

int main( void )
{
    func( "Hello", "World", 3 );
}
 

Вывод программы

 last3_1: llo,
last3_2: rld
 

Ответ №2:

 char last_three1[3]; // define two arrays that hold the last three characters
char last_three2[3];
 

Вам нужно место для хранения символа, потому %s что ожидается char* завершение.

 char last_three1[4]; 
char last_three2[4];

….
last_three1[x] = '';
x = 0; // repeat for the second string
for(int i = strlen(text2) - 4; i < strlen(text2) - 1; i  ) {
    last_three2[x] = text2[i];
    x  ;
}
last_three2[x] = '';
 

Ответ №3:

Строки в C заканчиваются одним нулевым байтом.

Вы не завершаете строки нулевым завершением, поэтому printf не знаете, где остановить печать (и вам повезло, что ваша программа не вылетает сразу).

Я бы реорганизовал подобные вещи, чтобы у вас была единственная функция, которая просто копирует три последних байта и завершает строку нулем:

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

// out must be an array that can hold 4 bytes
// in must be >= 3 characters; otherwise this function returns non-zero
int get_last_three(char *out, const char *in) {
  int len = strlen(in);
  if (len < 3)
    return -1;
  for (int i = len - 3; i < len; i  ) {
    *out = in[i];
    out  ;
  }
  *out = 0; // Zero-terminate
  return 0;
}

int main(int argc, char *argv[]) {
  char out[4];
  for (int i = 1; i < argc; i  ) {
    if (get_last_three(out, argv[i]) == 0) {
      printf("last3_%d: %sn", i, out);
    }
  }
}