プロが教える店舗&オフィスのセキュリティ対策術

非常に基礎的な質問で申し訳ないのですが
mainから渡した文字列を関数内で書き換えることができません。
int型の整数やchar型一文字はできるのですが。。。

例えば以下のようなソースでmainのABCをDEFに書き換えたいとき
どのようにすればいいのでしょうか。
(関数の戻り値で変更という方法以外で)
以下のソースでは値は書き換わりませんでした。

void func(char *str2)
{
str2 = "DEF";
}

int main()
{
char str1[20] = "ABC"

printf("%s", str1); //ABC
func(str1);
printf("%s", str1); //DEFになるようにしたい
}

A 回答 (6件)

ポインタの理解が足りませんね。


str2自体は仮引数なのでfunc関数から抜けると消滅します。
だからstr2をいくらいじっても無駄なのです。
ポインタそのものをいじるのじゃなくて、ポインタが指しているメモリに書き込まないとね。

void func(char *str2)
{
  strcpy(str2, "DEF");
}

これでOK

この回答への補足

ありがとうございます。

func関数から抜けると消滅するということは分かるのですが
int型の場合、参照で渡して
*str2 = 10;
のようにすればstr1も置き換わると思います。

char配列型の場合は似たような記述方法はないのでしょうか?
strcpyのような関数を使わなければ無理ですか?

補足日時:2011/08/07 10:57
    • good
    • 0

一文字ずつ変えたい場合は以下のようにして書き換えてください。


void func(char *str2)
{
str2[0] = "D";
str2[1] = "E";
str2[2] = "F";
}
    • good
    • 0
この回答へのお礼

配列に直接入れる必要があるのですね。
ありがとうございます。

お礼日時:2011/08/07 17:56

Cには値渡し(Call by value)しかないので、str2に直接値を代入しても呼び出し元では値が変わらないですね。


例えば、次のようにすると呼び出し元でも値が変わります。
void func(char *str2)
{
str2[0] = 'D';
str2[1] = 'E';
str2[2] = 'F';
}

Cの文字列変数は文字列の先頭文字に対するアドレスが入っているだけなので、funcが呼ばれたとき、str2にはmain()のstr1のアドレスが入っています。元の書き方ではこのstr2に"DEF"という文字列の先頭文字に対するアドレスを代入しているだけなので、main()のstr1の中身は変わりません。余談ですが、このようにアドレスを格納した変数のことをポインタといいます。

ちなみに、同じ事を文字列操作関数を使ってやるとこんなかんじです。
#include <string.h>
void func(char *str2)
{
strcpy(str2, "DEF");
}

この場合、呼び出し元はstr2の最大サイズを知らず、多めに書きこんでしまう可能性があります。そして、確保した容量以上にメモリーを使用するとプログラムを異常終了させたり、プログラマの意図しない誤った動きをさせたりすること (バッファ溢れ攻撃) が可能となります。
よって、この様なプログラムを書いたほうがよりよいでしょう。
#include <stdio.h>
#include <string.h>

void
func(char *str, size_t str_size)
{
strncpy(str, "DEF", str_size - 1);
str[str_size - 1] = '\0';
}

int
main(void)
{
char str1[20] = "ABC";

printf("%s\n", str1);
func(str1, sizeof(str1));
printf("%s\n", str1);

return 0;
}

strncpy(str, "DEF", str_size - 1);はstr_size - 1の長さだけ"DEF"を書き込む関数で、最期の一文字をNULL文字で終了させない場合があるのでstr[str_size - 1] = '\0';しないといけません。この問題を解決するためにstrlcpyという関数を代わりに使うケースもあります。
    • good
    • 0
この回答へのお礼

確かにサイズは気にしていなかったので
そういう危険性もあるのですね。

ありがとうございます。

お礼日時:2011/08/07 17:57

Cでは「文字列」という型は無くて、「ポインタ/配列が示す場所から0までのchar」を文字列として扱う、というルールで運用しています。


プログラムを作る際は、他の数値型とは分けて、他の数値型の配列やポインタを扱うようなつもりで考える必要があります。

たとえば
void func(int *n2)
{
int n0={1,2,3} ;
n2 = n0 ;
}
これが、実質何もしない、ということはおわかりですね? あなたの書いたfunc関数は上のintをcharの置き換えただけのものです。
    • good
    • 0
この回答へのお礼

文字列に関しての理解が浅かったです。
ありがとうございます。

お礼日時:2011/08/07 18:04

例えば



void testmain()
{
int i=0;

test(&i);
printf("%d",i);
}

void test(int*p)
{
*p=1;
}

の場合、&iが0x1000とすると、test内では0x1000の内容が1に変更されるため、testmainでもiの値が変わります。

ところが、

void testmain2()
{
char str[10] = "ABC";

test2(str);
printf("%s",str);
}

void test2(char* p)
{
p="DEF";
}

としたとき、strが0x2000、"DEF"の先頭アドレスが0x3000とすると、test2内ではpの値が0x3000に変更されるだけで、0x2000にあるデータは"ABC"のままです。当然、testmain2ではstrの値は0x2000なので、文字列は変更されません。
    • good
    • 0
この回答へのお礼

アドレスを代入と考えると分かりやすいですね。
ありがとうございます。

お礼日時:2011/08/07 18:05

>例えば以下のようなソースでmainのABCをDEFに書き換えたいとき、どのようにすればいいのでしょうか。



「ABC」を「DEF」の同じ連番記載に換えたいということですよね。
やり方は2通りあると思います。
1つはあなたが承知の文字定数「DEF」に換える場合と、もう一つは元の文字について一定の幅を持たせて任意の「DEF」に換える場合です。
前者の場合は #1 さんの strcpy()関数を使うのが良いと思います。ただし、string.hヘッダーファイルを include しなければなりません。
後者の場合は↓に示す方法で、与えられた文字列str2全部に対して一定の幅を持たせて変換するものです。

まあ、結局は好みの問題でもありますが...。



/* 定数3の理由:A,B,C,D -> 4番目のD-1番目のA */
void func(char *str2){
while(*str2) *str2++ += 3; //str2文字列の最後まで変換
}
    • good
    • 0
この回答へのお礼

すみません、ABCをDEFにというのは一例で
連番にしたいというわけではなかったです。
でも、連番だとそのような考え方もあるのですね。
ありがとうございます。

お礼日時:2011/08/07 18:06

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!