![](http://oshiete.xgoo.jp/images/v2/pc/qa/question_title.png?e8efa67)
まだまだCプログラマーの初心者です、よろしくお願いします。
次のコードについての質問です。
void swap_str(char **s1, char **s2)
{
char *tmp;
tmp = *s1;
*s1 = *s2;
*s2 = tmp;
}
int main(void)
{
char a1[15], a2[15];
puts("2つの文字列を入力");
printf("文字列a1: ");scanf("%s", a1);
printf("文字列a2: ");scanf("%s", a2);
swap_str(&a1, &a2);
puts("【2つの文字列の交換完了】");
printf("文字列a1: %s\n", a1);
printf("文字列a2: %s\n", a2);
return 0;
}
このコードをコンパイルすると以下のwarningが出ました。
xxxxx.c:26: warning: passing arg 1 of `swap_str' from incompatible pointer type
xxxxx.c:26: warning: passing arg 2 of `swap_str' from incompatible pointer type
(26行目は "swap_str(&a1, &a2);" になります)
関数swap_strの仮引数部分でポインタの型が矛盾した型を受け取っているのが原因だと思うのですがどうなのでしょうか?
一応実行してみると、1byte分は交換されていましたが、それ以上の部分は交換されてませんでした。
正しく交換するためにはどのように修正すればよろしいでしょうか?
実行環境:WindowsVista(32)、コンパイラーはgccです。
以上です、よろしくお願いします。
No.7ベストアンサー
- 回答日時:
すみません。
No.6です。5文字目以降は変換されませんでした。
次のプログラムを実行してみました。
#include <stdio.h>
void swap_str(char **s1, char **s2)
{
char *tmp;
tmp = *s1;
*s1 = *s2;
*s2 = tmp;
}
int main(void)
{
char a1[15], a2[15];
puts("2つの文字列を入力");
printf("文字列a1: "); scanf("%s", a1);
printf("文字列a2: "); scanf("%s", a2);
printf("%X\n%X\n", a1, a2); //a1, a2それぞれの先頭のアドレスを表示
swap_str(&a1, &a2);
puts("【2つの文字列の交換完了】");
printf("%X\n%X\n", a1, a2); //a1, a2それぞれの先頭のアドレスを表示
printf("文字列a1: %s\n", a1);
printf("文字列a2: %s\n", a2);
return 0;
}
このプログラムを実行すると、結果は次のようになります。
2つの文字列を入力
文字列a1: abcdefg
文字列a2: hijklmn
13FDCC <-a1の先頭のアドレス
13FDB4 <-a2の先頭のアドレス
【2つの文字列の交換完了】
13FDCC <-a1の先頭のアドレス
13FDB4 <-a2の先頭のアドレス
文字列a1: hijkefg
文字列a2: abcdlmn
見てわかるとおり先頭のアドレスは、a1もa2も変わってません。
これは、
char str[10];
と配列を定義した場合、確かにstrで先頭アドレスをあらわしますが、配列のアドレスは「定数」であるとC言語の仕様で決まっているため、swap_str関数のような処理を行って、後から変更することはできないのです。
四文字目まで変換された理由は、わかりません。
No.9
- 回答日時:
char は1バイトが基本です。
事例では a1[15] とのことですから、最大15バイトの文字格納数が確保されています。したがって a2[15] も同様の容量を持っているとの条件下における1バイトづつのバイト列の交換となり、どちらの文字列も文字列がなくなるまで swap することになります。なお、while (*s1 || *s2) は、while(!(*s1 == '\0' && *s2 == '\0')) のことで、Cならではの書き方に換えてあります。容量が気になる方は何バイト swap したのか文字要素数のチェック(SIZE以下であること)を入れると良いでしょう。
/* String swap program by Gcc on Mac OSX
* file name: kerberos.c
* compile: gcc kerberos.c
* execution: ./a.out
*/
#include <stdio.h>
#include <string.h>
#define SIZE15
void swap_str(char s1[], char s2[]) {
char temp;
while (*s1 || *s2) {
temp = *s1;
*s1++ = *s2;
*s2++ = temp;
}
}
int main(void) {
char a1[SIZE], a2[SIZE];
puts("2つの文字列を入力");
printf("a1: "); scanf("%s", a1);
printf("a2: "); scanf("%s", a2);
swap_str(a1, a2); //←訂正 swap_str(&a1, &a2);
puts("【2つの文字列の交換完了】");
printf("a1: %s\n", a1);
printf("a2: %s\n", a2);
return 0;
}
No.8
- 回答日時:
あなたのやりたいことは、もしかしたら次のコードが正しいのかもしれません。
(間違っていたらすいません)
int main(void)
{
char a1[15], a2[15];
char *str1, *str2; /*追加*/
puts("2つの文字列を入力");
printf("文字列a1: "); scanf("%s", a1);
printf("文字列a2: "); scanf("%s", a2);
str1 = a1; /*追加*/
str2 = a2; /*追加*/
swap_str(&str1, &str2); /*変更*/
puts("【2つの文字列の交換完了?】");
printf("文字列str1: %s\n", str1); /*変更*/
printf("文字列str2: %s\n", str2); /*変更*/
return 0;
}
以下は、混乱するかもしれませんので、参考程度に読んでください。
エラーの意味は、型が一致していないからです。
char a1[15];
で、&a1 の型は、正しくはchar (*)[15]になります。(配列へのポインタ)
(例)
char a1[15] = "hoge";
char **bad;
char (*good)[15];
bad = &a1; /* 間違い */
good = &a1; /* 正しい */
(*good)[1] = 'a'; /* a1の2文字目を書き換える */
分かりにくければ、こう考えてください。
typedef char strary[15]; /*strary は、char[15] を表す配列の型*/
strary *good; /* char (*good)[15]; と同じ意味*/
配列の変数名に&をつけても、多分、思っているようなものにはならないし、
プログラムを組む際も配列へのポインタは使用する機会はほとんどないと思います。
No.5
- 回答日時:
>>呼び出し側は「配列の先頭アドレス」を引数にしているためにワーニングが出ているのです。
>「配列の先頭アドレス」ではなく、「配列全体のアドレス」ではないでしょうか?
いえ、「先頭」であっています。
「配列」とは特定の型が(論理的に)「連続している状態」であって、別に物理的に連ぞしている必要はありません。
(もっとも、処理的には連続している方が都合がよいので、通常は物理的にも連続している状態を指していますが。)
また、char a[5]; と定義された場合a[0]が100番地であれば、
a[1]=101番地、a[2]=102番地、a[3]=103番地、a[4]=104番地となり、
この「100~105番地」が「配列全体」のアドレスとなります。
言葉尻をとらえているわけではなく、「ポインタ」が関係している為の説明ですので。
質問者さんのswap_str()は配列の内容全体を交換するものではないところかも「先頭アドレス」という表現は適切です。
No.4
- 回答日時:
#2 の最後のところに勝手に補足:
しばしば「配列名は (その配列の) 先頭要素へのポインタに変換される」といわれるのですが, これは正確ではありません. 正しくは, 「sizeof のオペランドになったときと (アドレスを求める演算子である) 単項 & のオペランドになったときを除いて」変換される, となります.
今の例ではまさにこの「除外される場合」なので, 変換されません... というか, 「a1」とか「a2」とかはプログラム上のみ存在する (つまり実行するときにはこの世のどこにも存在しない) ものなので, この関数で入れ替えることができたら逆に大変なことになってしまいます.
B だったらよかったのにね.
No.2
- 回答日時:
配列名だけを引数にすれば、アドレス渡しになりますので、わざわざポインタのポインタを使用する必要はありません。
void swap_str(char **s1, char **s2)
を
void swap_str(char *s1, char *s2)
とし、
swap_str(&a1, &a2);
は
swap_str(a1, a2);
とすれば、実際の領域を書き換えることができます。
これに伴い
char *tmp;
も
char tmp;
になります。
ただし、あなたのプログラムでは、最初の1文字しか入れ替えていないので、全ての文字を入れ替えるためには繰り返し処理を行う必要があります。C言語の場合、文字列の最後は\0になるので、このことを利用すればよいでしょう。
つまり、
while(*s1!='\0')
などとしてループさせればよいでしょう。
また、ループ中、ポインタをインクリメント(s1++など)することも忘れないでください。
ちなみに
あなたのプログラムで、&a1はchar**型ではなく、char(*)[15]型になります。char**は2次元配列とみなされます。2次元配列を受け取る場合、添え字計算の都合上横幅(この場合15)が必要になります。
配列は何次元配列であっても、メモリ上は1次元です。従って、2次元配列の添え字計算は 行番号×横幅+列番号 となり、どうしても横幅が必要になります。図に描くと解りやすいです。もし、ポインタのポインタを使用するのであれば、横幅を明示してください。
No.1
- 回答日時:
関数swap_strの引数が「ポインタへのポインタ」であるにもかかわらず、呼び出し側は「配列の先頭アドレス」を引数にしているためにワーニングが出ているのです。
よって、「ポインタへのポインタ」を渡すためには文字列を格納する領域を配列ではなく、動的メモリで確保してやります。下記にサンプルを示します。
------------------------------------------
void swap_str(char **s1, char **s2)
{
char *tmp;
tmp = *s1;
*s1 = *s2;
*s2 = tmp;
}
int main(void)
{
char *a1, *a2;
a1 = (char *)malloc(15);
a2 = (char *)malloc(15);
puts("2つの文字列を入力");
printf("文字列a1: "); scanf("%s", a1);
printf("文字列a2: "); scanf("%s", a2);
swap_str(&a1, &a2);
puts("【2つの文字列の交換完了】");
printf("文字列a1: %s\n", a1);
printf("文字列a2: %s\n", a2);
free(a1);
free(a2);
return 0;
}
この回答への補足
>呼び出し側は「配列の先頭アドレス」を引数にしているためにワーニングが出ているのです。
「配列の先頭アドレス」ではなく、「配列全体のアドレス」ではないでしょうか?
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# 宣言する関数の形が決まっている状態で、 str1とstr2の文字列をこの順に引っ付けてstrに保存し 2 2022/05/30 18:21
- C言語・C++・C# c言語配列の結合についてです。 なぜうまくいかないのでしょうか。 #include <stdio.h 4 2022/05/30 22:42
- C言語・C++・C# c言語 プログラムのエラー 1 2023/02/11 20:31
- C言語・C++・C# c言語でユーザ関数を利用して入力された文字列を反転させるプログラムを作りたいです。 3 2023/01/29 19:47
- C言語・C++・C# プログラミングの授業の課題です 1 2023/01/17 22:15
- C言語・C++・C# str[j++]の意味 2 2022/08/30 16:20
- C言語・C++・C# C#テキストボックスの文字を配列にいれてその後表示する 4 2022/07/17 04:47
- C言語・C++・C# C言語で再起関数とポインタを用いて文字列反転をする方法がわかりません。 4 2023/04/29 20:32
- C言語・C++・C# C言語プログラム変更 2 2022/12/21 15:03
- C言語・C++・C# C言語のエラーについて 2 2022/07/11 13:56
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
まだまだCプログラマーの初心者...
-
文字列内の数字削除
-
C言語のintとcharの違いってな...
-
文字列の反転
-
C言語のプログラムについてです
-
C言語にて構造体のメンバがNULL...
-
ポインタを使って回文かどうか...
-
文字列str内の全ての数字を...
-
fgetc( )の戻り値はなぜ整数??
-
%dなどの違い
-
関数から配列を返すには?
-
VBScriptで重複レコードを削除...
-
C言語 配列の長さの上限
-
c言語プログラミング 等差数列...
-
nullと""、\\0とEOFの違いにつ...
-
C言語のポインタに直接アドレス...
-
ポインタのミスでOS壊れるの...
-
アセンブラでのメモリの動的確...
-
構造体でchar name[]と*nameの...
-
C# DataTableの行をソートしてD...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
char*を初期化したいのですが
-
CStringからchar*への型変換に...
-
C言語のintとcharの違いってな...
-
C言語にて構造体のメンバがNULL...
-
小数点入りの文字列をfloat型に...
-
strcat関数を自作したいです
-
C言語のプログラムについてです
-
const char* s1とただのchar s1...
-
DWORDとcharの変換
-
char型にint型の数値を代入する。
-
文字列の途中から途中までを抽出
-
new charとnew char[N]の違いは?
-
文字列内の数字削除
-
csvファイルをfscanfで読み込む...
-
fgetc( )の戻り値はなぜ整数??
-
char 文字列型 の表現範囲が-12...
-
fstream型オブジェクトを関数の...
-
エクセルのMID関数は、C言語では?
-
ポインタを使って回文かどうか...
-
ポインタ配列
おすすめ情報