Почему я не могу скопировать массив при помощи '='?

Другой возможный способ:

var _timer;
clearTimeout(_timer);
_timer = setTimeout(function() {
    // Your code
}, 1000); // Delay for 1 s.
7
задан felideon 6 July 2017 в 17:59
поделиться

9 ответов

Может быть, я просто старый и сварливый, но другие ответы, которые я видел, похоже, полностью упускают суть.

C не выполняет присваивания массивов, точка. Вы не можете назначить один массив другому массиву простым присваиванием, в отличие от некоторых других языков (например, PL / 1; Pascal и многие его потомки - Ada, Modula, Oberon и т. Д.). В C нет строкового типа. В нем есть только массивы символов, и вы можете ' • копировать массивы символов (точно так же, как вы можете копировать массивы любого другого типа) без использования цикла или вызова функции. [Строковые литералы на самом деле не считаются строковыми типами.]

Единственный раз, когда массивы копируются, это когда массив встроен в структуру, и вы выполняете присваивание структуры.

В моей копии K&R 2nd Edition, в упражнении 1-19 запрашивается функция реверс (ы) ; в моем экземпляре K&R 1st Edition это было упражнение 1-17 вместо 1-19, но был задан тот же вопрос.

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

#include <string.h>
void reverse(char s[])
{
    int i = 0;
    int j = strlen(s) - 1;
    while (i < j)
    {
        char c = s[i];
        s[i++] = s[j];
        s[j--] = c;
    }
}

#ifdef TEST
#include <stdio.h>
int main(void)
{
    char buffer[256];
    while (fgets(buffer, sizeof(buffer), stdin) != 0)
    {
        int len = strlen(buffer);
        if (len == 0)
            break;
        buffer[len-1] = '\0';  /* Zap newline */
        printf("In:  <<%s>>\n", buffer);
        reverse(buffer);
        printf("Out: <<%s>>\n", buffer);
    }
    return(0);
}
#endif /* TEST */

Скомпилируйте это с помощью -DTEST, чтобы включить тестовую программу и без определения только функции reverse () .

С сигнатурой функции, указанной в вопросе, вы избегаете вызова strlen () дважды для каждой строки ввода. Обратите внимание на использование fgets () - даже в тестовых программах использовать gets () - плохая идея. Обратной стороной fgets () по сравнению с gets () является то, что fgets () не удаляет завершающую новую строку, где gets () делает. Положительные стороны fgets () заключаются в том, что вы не получаете переполнения массива и можете определить, нашла ли программа перевод строки или не хватило места (или данных) до появления новой строки.

Обратной стороной fgets () по сравнению с gets () является то, что fgets () не удаляет завершающую новую строку, где gets () делает. Положительные стороны fgets () заключаются в том, что вы не получаете переполнения массива и можете определить, нашла ли программа перевод строки или не хватило места (или данных) до появления новой строки.

Обратной стороной fgets () по сравнению с gets () является то, что fgets () не удаляет завершающую новую строку, где gets () делает. Положительные стороны fgets () заключаются в том, что вы не получаете переполнения массива и можете определить, нашла ли программа перевод строки или не хватило места (или данных) до появления новой строки.

20
ответ дан 6 December 2019 в 05:56
поделиться

Ваш массив tmp был объявлен в стеке , поэтому, когда ваш метод завершится, память, используемая для хранения значений, будет освобождена из-за scoping .

s = tmp означает, что s должен указывать на то же место в памяти, что и tmp . Это означает, что когда tmp освобождается, s по-прежнему будут указывать на теперь возможную недопустимую освобожденную ячейку памяти.

Этот тип ошибки называется висячий указатель .

Изменить: Это не висячий модификатор, как указано в комментариях к этому ответу. Проблема в том, что выражение s = tmp изменяет только то, на что указывает параметр, а не то, какой фактический массив был передан.

Кроме того, вы можете выполнить обратное действие за один проход и без выделения всего массива в памяти, просто поменяв местами значения по одному:

void reverse(char s[], int slen) {
    int i = 0;        // First char
    int j = slen - 2; // Last char minus \n\0
    char tmp = 0;     // Temp for the value being swapped

    // Iterate over the array from the start until the two indexes collide.
    while(i < j) {
        tmp = s[i];  // Save the eariler char
        s[i] = s[j]; // Replace it with the later char
        s[j] = tmp;  // Place the earlier char in the later char's spot
        i++;         // Move forwards with the early char
        j--;         // Move backwards with the later char
    }
}
8
ответ дан 6 December 2019 в 05:56
поделиться

To round out the discussion here are two other possible ways to reverse as string:

void reverse(char string1[], char string2[])
{
  int i = 0, len = 0;

  while(string2[len] != '\0')   // get the length of the string
      len++;

  while(len > 0)
  {
    string1[i] = string2[len-1]; // copy the elements in reverse
    i++;
    len--;
  }
  string1[i] = '\0'; // terminate the copied string 
}

Or recursively:

void reverse (const char *const sPtr)
{
  //if end of string
  if (sPtr[0] == '\0')
  {
    return;
  }
  else  //not end of the string...
   {
    reverse(&sPtr[1]);  //recursive step
    putchar(sPtr[0]);   //display character
   }
}
1
ответ дан 6 December 2019 в 05:56
поделиться

because tmp is a pointer, and you need to get a copy, not a "link".

0
ответ дан 6 December 2019 в 05:56
поделиться

В случае s = tmp значение of tmp, который также является начальным адресом массива, будет скопирован в s.

Таким образом, и s, и tmp будут указывать на один и тот же адрес в памяти, что, я думаю, не является целью.

Ура

0
ответ дан 6 December 2019 в 05:56
поделиться

Попробуйте поэкспериментировать и посмотрите, что происходит, когда вы делаете что-то вроде этого:

void modifyArrayValues(char x[], int len)
{
    for (int i = 0; i < len; ++i)
        x[i] = i;
}

void attemptModifyArray(char x[], int len)
{
    char y[10];
    for (int i = 0; i < len; ++i)
        y[i] = i;
    x = y;
}


int main()
{
    int i = 0;
    char x[10];
    for (i = 0; i < 10; ++i)
        x[i] = 0;

    attemptModifyArray(x, 10);
    for (i=0; i < 10; ++i)
        printf("%d\n", x[i]); // x is still all 0's

    modifyArrayValues(x, 10);
    for (i=0; i < 10; ++i)
        printf("%d\n", x[i]); // now x has 0-9 in it
}

Что происходит, когда вы изменяете массив непосредственно в tryModifyArray , вы просто перезаписываете локальную копию адреса массива x . Когда вы вернетесь, исходный адрес все еще будет находиться в копии x main .

Когда вы изменяете значения в массиве в modifyArrayValues ​​, вы изменяете сам фактический массив, адрес которого хранится в modifyArrayValues ​​ локальной копии x . Когда вы вернетесь, x по-прежнему удерживает тот же массив, но вы изменили значения в этом массиве.

0
ответ дан 6 December 2019 в 05:56
поделиться

В этом потоке есть интересный подпоток о массивах и указателях. Я нашел эту ссылку в Википедии со своеобразным фрагментом кода, показывающим, насколько «пластилиновым» может быть Си!

/* x designates an array */
x[i] = 1;
*(x + i) = 1;
*(i + x) = 1;
i[x] = 1; /* strange, but correct: i[x] is equivalent to *(i + x) */

Конечно, еще больше сбивает с толку в C то, что я могу делать это:

unsigned int someval = 0xDEADD00D;
char *p = (char *)&someval;

p[2] = (char)0xF0;

Таким образом, взаимозаменяемость указателей и массивов кажется настолько глубоко укоренившейся в языке C, что почти преднамеренной.
Что думают все остальные?

--- Исходное сообщение ---
s и tmp являются указателями, поэтому выполнение s = tmp просто заставит s указывать на адрес, по которому tmp находится в памяти.
Другая проблема с тем, что вы обрисовали в общих чертах, заключается в том, что tmp является локальной переменной, поэтому она станет неопределенной, когда она выйдет за пределы области видимости, т.е. когда функция вернется.

Убедитесь, что вы полностью усвоили эти три концепции, и вы не ошибетесь

  1. Область действия
  2. Разница между стеком и кучей
  3. Указатели

Надеюсь, это поможет, и продолжайте!

0
ответ дан 6 December 2019 в 05:56
поделиться

Очень простой ответ был бы - и s, и tmp являются указателями на ячейку памяти, а не сами массивы. Другими словами, s и tmp - это адреса памяти, где хранятся значения массива, но не сами значения. И один из распространенных способов доступа к этим значениям массива - использование индексов типа s [0] или tmp [0].

Теперь, если вы попытаетесь просто скопировать, s = tmp, адрес памяти массива tmp будет скопировать в s. Это означает, что исходный массив s будет утерян, и даже указатель памяти s теперь будет указывать на массив tmp.

Вы хорошо поймете эти концепции со временем, так что продолжайте читать книгу. Надеюсь, это простое объяснение поможет.

-2
ответ дан 6 December 2019 в 05:56
поделиться

Because both s and tmp are memory addressees. If you s = tmp, both pointers would point to the same array.

Suppose that we have

char s[] ="ab"; 

/*
* Only for explanatory purposes.
* 
*/
void foo(char s[]){ 
    char tmp [] = "cd";
    s= tmp;
 }

foo(s);

after s= tmp you would have

s[0] : 'c'
s[1] : 'd'
s[2] : '\0'

Even though both arrays have the same data, a change in tmp, will affect both of them, because both arrays are actually the same. They both contain data that´s in the same memory address. So by changing any position of the tmp array, or destroying the tmp array, s would be affected in the same way.

By looping over the array, what you are doing is moving a piece of data from one memory address to another.

In my copy of K & R, pointers are explained in chapter 4. A quick glance through the first pages may be of help.

4
ответ дан 6 December 2019 в 05:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: