3

私はCを使い始めたばかりで、以前使用していた言語ではポインタを扱う必要がなかったので、文字列を変更するだけならどの方法が良いのか疑問に思っていました.

ポインターストリングと通常。

また、いつポインターを使用するかについての詳細情報を提供したい場合も、それは素晴らしいことです。関数「通常」が渡された文字列を変更し、戻り値なしでメイン関数を更新することを知ったとき、私はショックを受けました。

#include <stdio.h>

void pointerstring(char *s);
void normal(char s[]);

int main() {
    char string[20];
    pointerstring(string);
    printf("\nPointer: %s\n",string);
    normal(string);
    printf("Normal: %s\n",string);
}

void pointerstring(char *s) {
    sprintf(s,"Hello");
}
void normal(char s[]) {
    sprintf(s,"World");
}

出力:

Pointer: Hello
Normal: World
4

4 に答える 4

7

関数宣言では、char []char *は同等です。外部レベルの配列型を持つ関数パラメーターは、同等のポインター型に変換されます。これは、呼び出しコードと関数本体自体に影響します。

このため、構文を使用することをお勧めします。そうしないと、混乱して外部レベルの固定長配列型パラメーターchar *を取得しようとする可能性があります。sizeof

void foo(char s[10]) {
    printf("%z\n", sizeof(s));  // prints 4 (or 8), not 10
}

ポインターとして宣言されたパラメーターを関数に渡す (およびポインター パラメーターが宣言されていないconst) 場合、ポインターが指すオブジェクトまたは配列を変更する権限を関数に明示的に与えています。

于 2012-08-23T10:20:07.110 に答える
5

C の問題の 1 つは、配列が二流市民であることです。ほとんどすべての有用な状況で、配列を関数に渡すときに、配列はポインターに分解されます(そのため、サイズ情報が失われます)。

したがって、配列をT* argorとして使用しても違いはありませんT arg[]後者は前者の単なる同義語ですstringどちらも で定義された変数の最初の文字へのポインタであるmain()ため、どちらも元のデータにアクセスして変更できます。


注: C は常にcopy ごとに引数を渡します。この場合も同様です。ただし、ポインター (またはポインターに減衰する配列) を渡すと、コピーされるのは addressであるため、参照されるオブジェクトには、そのアドレスの 2 つの異なるコピーを介してアクセスできます。

于 2012-08-23T10:21:15.017 に答える
1

ポインタありとポインタなし

1)関数に値を送信して関数から値を返す代わりに、ローカル変数reference(address)を新しい関数に直接渡して、値を処理および更新できます。

ポインタ付き

...
int a = 10;
func(&a);
...

void func(int *x);
{
   //do something with the value *x(10)
   *x = 5;
}

ポインタなし

...
int a = 10;
a = func(a);
...

int func(int x);
{
   //do something with the value x(10)
   x = 5;
   return x;
}

2)グローバル変数または静的変数には有効期間のスコープがあり、ローカル変数には関数のみのスコープがあります。ユーザー定義のスコープ変数を作成する場合は、ポインターが必要であることを意味します。つまり、いくつnかの関数でスコープを持つ必要がある変数を作成する場合は、最初の関数でその変数のダイナミックメモリを作成し、それをすべての関数に渡し、最後にn番目の関数でメモリを解放します。

3)メンバー関数をメンバー変数と一緒に構造内に保持したい場合は、関数ポインターを使用できます。

struct data;

struct data
{
    int no1, no2, ans;
    void (*pfAdd)(struct data*);
    void (*pfSub)(struct data*);    
    void (*pfMul)(struct data*);
    void (*pfDiv)(struct data*);
};

void add(struct data* x)
{
   x.ans = x.no1, x.no2;
}
...
struct data a;
a.no1 = 10;
a.no1 = 5;
a.pfAdd = add;
...
a.pfAdd(&a);
printf("Addition is %d\n", a.ans);
...

4)dataサイズsが非常に大きい構造を考えてみましょう。この構造体の変数を別の関数に送信する場合は、参照として送信する方が適切です。これにより、新しい関数用に作成されたアクティベーションレコード(スタック内)のサイズが減少するためです。

ポインターあり-関数のアクティベーションレコード(スタック内)に必要なのは4バイト(32ビットm / c)または8バイト(64ビットm / c)のみです。func

...
struct data a;
func(&a);
...

ポインタなし-s関数のアクティベーションレコード(スタック内)にバイトが必要になりますfunc。非常に大きな価値sがあると考えてください。sizeof(struct data)

...
struct data a;
func(a);
...

5)定数変数の値をポインタで変更できます。

...
const int a = 10;
int *p = NULL;
p = (int *)&a;
*p = 5;
printf("%d", a); //This will print 5
...
于 2012-08-23T13:29:23.407 に答える
0

他の回答に加えて、「文字列」操作関数(文字列=ゼロで終了するchar配列)に関する私のコメント:常に文字列パラメーターを戻り値として返します。

したがって、次のように、関数proceduralまたはfunctionalを使用できます。printf("Dear %s, ", normal(buf));

于 2012-08-23T12:55:13.000 に答える