Як пройти через покажчик і виділити місце на купу

Я намагаюся додати вказівник символу до функції, яка потім заповнює її матеріалом, так що функція виклику може використовувати її тоді. Оскільки це завжди давало мені дивні речі в функції виклику, я написав просте уявлення про те, що я зробив:

#include 
#include 

void bla(char *t)
{
    t = (char*) malloc(5);
    if (t != 0) {
        t[0] = 'h';
        printf("%c\n", t[0]);
    }
}

int main(int argc, char **argv)
{
    char b[10];

    bla(b);
    printf("%c\n", b[0]);
    return 1;
}

Я не зовсім впевнений, що у нього щось робити, що С передає копію аргументу. Чи потрібно мені передавати покажчик на покажчик, чи є краще рішення?

EDIT:

Вибач, хлопці, але я цього не отримав. Не могли б ви переглянути цей приклад:

#include 
#include 

void blub(char **t)
{
    printf("%d\n", *t);
    *t = (char*) malloc(sizeof(char) * 5);
    *t[0] = 'a';
    *t[1] = 'b';
    printf("%d\n", *t);
    printf("%d\n", *t[0]);
    printf("%d\n", *t[1]);
}

int main(int argc, char **argv)
{
    char *a;
    blub(&a);
    printf("%d\n", a);
    printf("%d\n", a[0]);
    printf("%d\n", a[1]);

    return 1;
}

Висновок виглядає наступним чином:

./main
6154128
140488712
97
98
140488712
97
0      <== THIS SHOULD BE 98 AS ABOVE!?

Чому я отримую 98 в функції blafu і в основному це нульовий покажчик ?! Я цілковито заплутався: /

0
@pluckyDuck - див. мій редагування :) Ви в правильному напрямку.
додано Автор Kiril Kirov, джерело
цей код не має сенсу ...
додано Автор Mitch Wheat, джерело
Не забудьте дещо впустити свою пам'ять!
додано Автор Nick, джерело

6 Відповіді

Вам потрібно використовувати покажчик на покажчик.

//............vv
void bla(char **t)

Потім скористайтеся вказівником, вимкнувши його:

// note the '*' in front of t
*t = (char*) malloc(5);
if (*t != 0) {
    *t[0] = 'h';
    printf("%c\n", *t[0]);
}

Крім того, оголошуйте b як char * , а не як char b [10] .


Чому? Тому що ви намагаєтеся змінити покажчик. Логіка така сама, як і інші типи, але це незрозуміло тут.
Подумайте про це так: якщо вам потрібно передати int , і вам потрібно буде змінити його, вам знадобиться вказівник на int . Те саме тут - потрібно передати покажчик на символ , і вам потрібно змінити його, тому використовуйте " покажчик на покажчик на символ " :)


EDIT: According to your edit - yes, you have understood this perfectly, there's just a small problem - the priority of operator* and operator[]. If you replace your *t[X] with (*t)[X], everythng will be fine :)

7
додано
ти врятував мені життя :)
додано Автор pluckyDuck, джерело

Якщо ви використовуєте покажчик на покажчик

void blah(char **t)

ви можете призначити шматочок пам'яті (у голові) за допомогою malloc як

*t = malloc(size);

і ти називаєш Бла так:

char *b;
bla(&b);

і він буде виділений і наповнений використанням в бла, і результат може бути надрукований в основному.

2
додано

Ви проходили покажчик, щоб працювати. Через це ви можете назавжди змінити (після повернення функції) лише об'єкт, на який він вказує, але ви не можете змінити покажчик саме так, як це копія, яка насправді передана для функціонування. Всі зміни всередині функції виконуються на копії покажчика. Його вихідне значення залишається незмінним, коли функція повертається.

(1) Якщо ви хочете змінити деяку змінну в деякій функції, вам потрібно передати його вказівник. Якщо ця змінна має тип покажчика, то пропустіть вказівник на нього - покажчик на покажчик! У вашому випадку ця змінна буде мати тип char **.

(2) Якщо ви хочете виділити пам'ять на купі цієї функції, вам не потрібно використовувати стек - оголосити вказівник b як char *.

(3) Після виділення пам'яті на купу, не забудьте вилучити його, інакше ви потрапляєте до витоків пам'яті.

(4) Повернення 0 з вашої програми, якщо воно закінчується без помилок. Зберігайте інші повернуті значення для кодів помилок - у випадку різних помилок, які можуть виникнути у вашому додатку, повертайте різні коди.

The final point is that this is a pure C application. In C++ you would use new/delete to allocate/deallocate memory, std::string instead of char* for strings, and std::cout << instead of printf for sending text to the output stream

void bla(char **pt)              //(1)
{
    *pt = (char*)malloc(5);

    if (*pt != 0) 
    {
        *pt[0] = 'h';
        printf("%c\n", *pt[0]);
    }
}

int main(int argc, char **argv)
{
    char* b = 0;                 //(2)

    bla(&b);
    printf("%c\n", b[0]);

    if(b)
    {
        free(b);                 //(3)
        b = 0;
    }

    return 0;                    //(4)
}
1
додано

Якщо ви хочете повернути результат, використовуючи посилання var, тоді так, вам доведеться передати вказівник на покажчик (або просто повернути результат звичайним способом, char * bla ()). Не забудьте нульові термінатори!

0
додано

Коли ви пишете char b [10]; у вас є статично виділений масив, вам не потрібно більше виділяти його, це може мати до 10 char значення.

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

void bla(char *t)
{
    if (t != 0) {
        t[0] = 'h';
        printf("%c\n", t[0]);
    }
}

Після функції значення b у вашому main також змінюється.

0
додано
це була лише модель для реальної проблеми, і там мені потрібно використовувати malloc.
додано Автор pluckyDuck, джерело

Причиною дивної поведінки у другому прикладі є пріоритет операторів. [] (Кронштейни, індексний масив) має більш високий пріоритет, ніж * (dereference)

So, *t[1] is the same as *(t[1]). You need (*t)[1]

оскільки t є покажчиком на покажчик на char:

*(t[1]) means "take t's address, go (sizeof(char*)) bytes ahead, dereference the address, and dereference again"

(*t)[1] means "take t's address, dereference it, go (sizeof(char)) bytes ahead and dereference again"

Насправді, в першому випадку, поведінка невизначена, оскільки вона намагається подвоїти дефіцит місця після t. У вашому прикладі перше значення завжди буде надруковано, тому що t [0] означає лише вилучення, тому два випадки будуть однаковими.

0
додано