C: Если, как я понимаю, 0 и '\ 0' одинаковы, как компилятор узнает размер массива, когда я пишу int my_array = {0} ;? - PullRequest
2 голосов
/ 19 июня 2019

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

Так что в основном это работает, но это не работает, если я использую целевой массив следующим образом:

int dest_array [10] = {0};

Из того, что я понимаю, он заполняет массив int 0, которые эквивалентны '\ 0' (нулевые символы) Итак, вот мой вопрос:

В таком случае, как компьютер может узнать размер массива или когда он заканчивается?

(А как сравнить массивы, переданные в качестве параметров?)

void copy(int *src_arr, int *dest_arr)
{
    // The advantage of using pointers is that you don't need to provide the source array's size

        // I can't use sizeof to compare the sizes of the arrays because it does not work on parameters.
        // It returns the size of the pointer to the array and not of of the whole array

    int* ptr1;
    int* ptr2;

    for(     ptr1 = source, ptr2 = dest_arr ;
        *ptr1 != '\0'              ;        
             ptr1++, ptr2++            )
    {   
        if(!*ptr2) // Problem here if dest_arr full of 0's
                { 
                     printf("Copy interrupted :\n" +
                            "Destination array is too small"); 
                     break; 
                }

        *ptr2 = *ptr1;
    }

Ответы [ 4 ]

3 голосов
/ 19 июня 2019

В C невозможно узнать длину массива по своей сути.Это связано с тем, что массив на самом деле представляет собой просто непрерывный кусок памяти, а значение, переданное функциям, на самом деле является просто указателем на первый элемент в массиве.В результате этого, чтобы на самом деле узнать длину массива внутри функции, отличной от функции, в которой был объявлен этот массив, вы должны каким-то образом передать это значение функции.Два распространенных подхода - это использование значений часовых, которые указывают на последний элемент (аналогично тому, как NULL используется в качестве последнего символа строки в C), или предоставление другого параметра, который содержит длину массива.

Asочень распространенный пример этого: если вы написали какие-либо программы, использующие параметры командной строки, то вы наверняка знакомы с общим определением int main(int argc, char *argv[]), которое использует второй из вышеупомянутых подходов, предоставляя длину argv массив через параметр argc.

У компилятора есть несколько способов обойти это для локальных переменных.Например, будет работать следующее:

#include <stdio.h>

int main(){
    int nums[10] = {0};
    printf("%zu\n", sizeof(nums)/sizeof(nums[0]));

    return 0;
}

Который печатает 10 в STDOUT;однако это работает только потому, что операция sizeof выполняется локально, и компилятор знает длину массива в этой точке.

С другой стороны, мы можем рассмотреть ситуацию передачи массива другомуфункция:

#include <stdio.h>

int tryToGetSizeOf(int arr[]){
    printf("%zu", sizeof(arr)/sizeof(arr[0]));
}

int main(){
    int nums[10] = {0};
    printf("%zu\n", sizeof(nums)/sizeof(nums[0]));

    puts("Calling other function...");
    tryToGetSizeOf(nums);

    return 0;
}

Это приведет к выводу на STDOUT следующего:

10
Calling other function...
2

Это может быть не то значение, которое вы ожидаете, но это происходит из-за того, чтосигнатура метода int tryToGetSizeOf(int arr[]) функционально эквивалентна int tryToGetSizeOf(int *arr).Следовательно, вы делите размер целочисленного указателя (int *) на размер одного int;в то время как вы все еще находитесь в локальном контексте main() (, т.е., где массив был определен изначально ), вы делите размер выделенной области памяти на размер типа данных этой области памятиразделен как (int).

Пример этого доступен на Ideone .

2 голосов
/ 19 июня 2019
int* ptr1;
int* ptr2;

Вы теряете информацию о размере, когда называете массивы указателями.Невозможно определить размер массива, т. Е. Количество элементов, используя ptr1.Вы должны воспользоваться помощью другой переменной, которая будет обозначать размер массива, на который ссылается ptr1 (или ptr2).

То же самое относится и к массивам символов.Рассмотрим следующее:

char some_string[100];
strcpy(some_string, "hello");

Подход, упомянутый вами при проверке \0 (или 0), дает вам количество элементов, которые являются частью строки, находящейся в some_string.Это никоим образом не относится к количеству элементов в some_string, равному 100.

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

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

0 голосов
/ 19 июня 2019

В C есть понятие полный и неполный массив .Вы можете использовать синтаксический сахар при инициализации arr[X] = {0} только для полных массивов.

0 голосов
/ 19 июня 2019

TL / DR - вам нужно будет передать размер массива в качестве отдельного параметра вашей функции. Значения Sentinel, такие как 0, отмечают только логический конец последовательности, а не конец самого массива.

Если это не операнд операторов sizeof или унарных &, или строковый литерал, используемый для инициализации массива символов в объявлении, выражение типа "массив N-элементов из T "будет преобразовано (" распад ") в выражение типа" указатель на T ", а значением выражения будет адрес первого элемента массива. Поэтому, когда вы передаете исходные и конечные массивы в качестве аргументов copy, функция фактически получает всего два указателя.

Нет метаданных, связанных с указателем, который сообщает ему, указывает ли он на первый объект в последовательности или какова длина этой последовательности 1 . Значение часового типа, например, терминатор 0 в строках, говорит только о длине логической последовательности значений, а не о размере массива, в котором они хранятся 2 .

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


То же самое верно для объектов массива - в объекте массива нет метаданных времени выполнения для хранения размера или чего-либо еще. Единственная причина, по которой работает трюк sizeof, заключается в том, что объявление массива находится в области видимости. Сам объект массива не знает, насколько он большой. Это проблема для библиотечных функций, таких как strcpy, которые получают только начальный адрес для каждого буфера - если в исходном буфере больше символов, чем предназначено для хранения, strcpy будет пролетать сразу после конец целевого буфера и перезаписать все, что следует.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...