2009-03-03 10 views
8

文字列の配列内の1文字をどのように扱うかを理解しようとしています。また、これはもちろん、私が一般的に添え字を付けるポインタへのポインタを理解することを可能にします。 もし私がchar **aを持っていて、2番目の文字列の3番目の文字に到達したいのであれば、これは動作しますか:**((a+1)+2)?それがそうであるように思われる...cで文字列の配列から個々の文字にアクセスするにはどうすればよいですか?

答えて

19

ほとんど、しかし全くありません。正解は次のとおりです。

*((*(a+1))+2) 

あなたは、実際の文字列ポインタの1への最初のデリファレンスする必要があり、その後、あなたがデリファレンスしたい文字までの文字列ポインタを選択したため。 (ここでは、操作の順番を明確にするために括弧を付け加えています)。

また、この式:

a[1][2] 

あなたがやろうとしているものの意図がより自明であると表記自体がより簡潔であるため、また....動作し、おそらく好ましいであろう! 。この形式は新しい言語の人にはすぐには分かりませんが、配列記法が機能する理由は、C言語では配列の索引付け操作が、同等のポインタ操作の省略形にすぎないためです。すなわち:*(a + x)はa [x]と同じです。したがって、その論理を元の質問に拡張することによって、式a [x] [y]は、一般的な形式の*((*(a + x))+に相当する2つの別々のポインタ逆参照演算が連鎖的にカスケードされます。 y)。

+0

したがって、ポインターへのポインターは、他の宣言もまったくない配列として使用できますか? – sdellysse

+0

理由を説明すると+1します。舞台裏で –

+0

、[b]は*(a + b)に翻訳されます –

1

Try a[1][2]または*(*(a+1)+2)

基本的に、配列参照はポインタ逆参照用の構文的砂糖です。 a [2]はa + 2と同じで、2 [a]と同じです(本当にコードを読めない場合)。文字列の配列は、ダブルポインタと同じです。したがって、[1]または*(a+1)のいずれかを使用して2番目の文字列を抽出することができます。 b(2)または*(b + 2)のいずれかを使用して、その文字列の3番目の文字を見つけることができます(今のところ 'b'と呼んでください)。最初の2番目の文字列を 'b'に置き換えると、[1] [2]または*(*(a+1)+2)のいずれかになります。

2

IIRC、文字列は、実際には文字の配列であるので、これは動作するはずです:

a[1][2] 
3

あなたは、ポインタを使用する必要はありません。 (int型ARGC、チャー** ARGV){

のprintf(主

INT、ARGV [1] [2 " ARGVの3番目の文字は、[1]〜[%のC]。\ nは" ]);そして

}

$ ./mainハロー ARGVの3番目の文字[1]〜[L]です。

これは1と1です。

必要に応じてポインタを使用できます...

*(ARGV [1] +2)

あるいは

*((*(A + 1))+ 2)

として誰か上で指摘した。

これは、配列名がポインタであるためです。 Cポインタにウィキペディアarticleから

+0

ペンデントリ:argv [1] [3] argv [1]の4番目の文字の場合... – dmckee

+0

ええ。私はちょうどそれを変えた。それは私が素早く投稿し、私の下付き文字をチェックしないために得られるものです。 –

2

引用 - Cにおいて

、配列のインデックスが正式にポインタ演算に関して定義されます。つまり、 の言語仕様では、array [i]が*(array + i)と同等であることが必要です。したがって、Cでは、配列は、連続するメモリ領域(ギャップなし)へのポインタ、 と考えることができ、配列にアクセスするための構文は、 ポインタを逆参照するために使用できるものと同じです。例えば、配列は次のように宣言して使用することができます。

int array[5];  /* Declares 5 contiguous (per Plauger Standard C 1992) integers */ 
int *ptr = array; /* Arrays can be used as pointers */ 
ptr[0] = 1;  /* Pointers can be indexed with array syntax */ 
*(array + 1) = 2; /* Arrays can be dereferenced with pointer syntax */ 

だから、あなたの質問に応じて - はい、ポインタへのポインタは、他の宣言のいずれかの種類なしの配列として使用することができますすべて!

1

本書の華麗なCプログラミングの説明The Explacy of the Explorationの第2版Jon Erickson著、プログラミングの説明セクションだけで言及するポインタ、文字列について説明しています。https://leaksource.files.wordpress.com/ 2014/08/hacking-the-art-of-exploitation.pdf。

質問には既に答えがありましたが、もっと知りたいと思っている人は、Erickonsの本の中のいくつかの構造を理解するのに役立つ次のハイライトを見つけるかもしれません。あなたはおそらく使用する変数操作のために利用可能なヘッダファイルの

ヘッダ

例。

stdio.hの - http://www.cplusplus.com/reference/cstdio/

STDLIB.H - http://www.cplusplus.com/reference/cstdlib/

文字列。時間 - http://www.cplusplus.com/reference/cstring/

limits.hに - http://www.cplusplus.com/reference/climits/

機能

おそらく使用する汎用関数の例。

のmalloc() - http://www.cplusplus.com/reference/cstdlib/malloc/

のcalloc() - http://www.cplusplus.com/reference/cstdlib/calloc/

strcpy() - http://www.cplusplus。COM /参照/のCString/strcpyの/

メモリ

"コンパイルされたプログラムのメモリは、5つのセグメントに分割される。各セグメントは、メモリの特別な部分を表すテキスト、データ、BSS、ヒープ、スタック。テキストセグメントはコードセグメントと呼ばれることもあり、プログラムのアセンブルされた機械語命令は "となります。

"このセグメント内の命令の実行は、コンパイル分岐、ジャンプに 、およびアセンブリ言語の指示を呼び出す前述のハイレベルの制御構造及び機能のおかげで、非線形である。プログラムとして 実行しますEIPは、テキストセグメント内の最初の命令に設定されている プロセッサは、その後、次の処理を行い、実行ループ以下:

」が 1. EIPは

」を指している命令を読み込みを 2. "

" 3. 4. 1 "

ステップに戻るステップ1 "

" で読み出した命令を実行EIPに命令のバイト長を加算します

"命令がジャンプまたはコール命令である場合があります。 は、EIPを別のメモリアドレスに変更します。プロセッサーは の実行が非線形であると予想しているので、変更を気にしません。 ステップ3でEIPが変更された場合、プロセッサはステップ1に戻り、変更されたEIPのアドレスにある命令をに読み込みます。

"テキストセグメントでは書き込み許可が無効です。変数を格納するのには使用せず、コードのみを使用します。これにより、実際にプログラムコードを修正することができなくなります。メモリのこのセグメントに書き込むと、プログラムは何か不具合が発生したことをユーザーに警告し、プログラム が殺されます。このセグメントが読取り専用であることの別の利点は、 をプログラムの異なるコピー間で共有できるため、プログラムの複数の実行が問題なく同時に実行できることです。 また、このメモリ・セグメントは、何もするので、中にこれまで 変化を一定の大きさを持っていることに留意すべきであること

データとBSSセグメントはグローバルと静的プログラムに 変数を格納するために使用されています。データセグメントは初期化されたグローバルおよびスタティック変数で満たされ、bssセグメントは初期化されていない対応変数で埋められます。これらのセグメントは書き込み可能ですが、固定サイズもあります。関数のコンテキスト(前の例の変数jのようなもの)にもかかわらず、グローバル変数は永続的であることに注意してください。彼らは自分のメモリセグメントに格納されているため、変数が持続することができ、グローバルおよび静的の両方「

ヒープ・セグメントは、メモリのセグメントプログラマができる直接 制御です。このセグメントのメモリブロックは、プログラマが必要とするものであれば、 に割り当てて使用できます。ヒープ セグメントについての一つの注目すべき点は、を必要に応じてそれを大きくしたり小さく成長することができますので、それは、一定の大きさのされていないということです「

ヒープ内のメモリのすべてはアロケータとデアロケータアルゴリズムによって管理されていますヒープ内のメモリ領域をそれぞれ予約して使用し、予約を削除して、メモリのその部分を後の予約のために再利用できるようにする。ヒープは、多くのメモリがどのように使用するために予約されているかによって大きくなり、縮小します。これは、ヒープ 割り振り関数を使用するプログラマーが、オンザフライでメモリーを予約して解放できることを意味します。ヒープが下方より高いメモリに向かって移動 の成長がに対処する「

スタックセグメントは、可変サイズを有しており、関数呼び出し時にローカル機能変数とコンテキストを保存するために、一時的なスクラッチパッドとして使用されます。これは、GDBのバックトレースコマンドが見るものです。プログラムが関数を呼び出すとき、その関数はそれ自身の渡された変数のセットを持ち、関数のコードはテキスト(またはコード)セグメントの異なるメモリ位置にあります。関数が呼び出されると、コンテキストとEIPは変更される必要があるため、渡されたすべての変数、関数の終了後にEIPが返す位置、およびその関数が使用するすべてのローカル変数を記憶するためにスタックが使用されます。これらの情報はすべてスタック上にまとめてスタックフレームと呼ばれます。スタックは多くのスタックフレームを含む "

" 一般的にコンピュータ科学用語では、スタックは頻繁に使用される抽象的なデータ構造です。ファーストイン、ラストアウト(FILO)注文 があります。これは、スタックに入れられる最初のアイテムが最後に出てくるアイテムであることを意味します。一方の端に結び目がある紐の上にビードを置くと考えてください。他のすべてのビードを取り除くまで、最初のビードを取り除くことはできません。項目がスタックに置かれている場合、それはプッシュとして知られている、項目がスタックから削除されたとき、それは飛び出ると呼ばれています「

名前が示すように、メモリのスタックセグメントは、実際には、あります、スタックフレームを含むスタックデータ構造。 ESPレジスタは、スタックの最後のアドレスを追跡するために使用されます。スタックのアドレスは、アイテムがプッシュされたりポップされたりすると常に変化します。これは非常に動的な動作なので、スタックも固定サイズでないことが理にかなっています。反対ヒープのダイナミックな成長への下メモリがをアドレスに向かって、大きさのスタック切り替え sと、それは、メモリの視覚的なリストに上向きに成長する「

スタックのFILOの性質は奇妙に思えるかもしれませんしかし、スタックはコンテキストを格納するために で使用されるので、非常に便利です。関数が呼び出されると、スタックフレーム内で複数のものがスタックに一緒にプッシュされます。現在のスタックフレーム内のローカル関数変数を参照するために、フレームポインタ(FP)またはローカルベース(LB)ポインタと呼ばれることもあるEBPレジスタが使用されます( )。各スタックフレームには、関数へのパラメータ、そのローカル変数、および保存されたフレームポインタ(SFP)と戻りアドレスを元に戻すために必要な2つのポインタが含まれています。 SFPを使用してEBPを以前の値に復元し、復帰アドレス を使用して、EIPを関数呼び出し後の次の命令に復元します。これは、前のスタックフレーム の機能コンテキストを復元「

ストリング

」Cにおいて、アレイは、単に特定のデータ型のn個の要素のリストです。 20文字の配列は、メモリ内にある20個の隣接文字です。アレイはまたバッファと呼ばれる「

#include <stdio.h> 

int main() 
{ 
    char str_a[20]; 
    str_a[0] = 'H'; 
    str_a[1] = 'e'; 
    str_a[2] = 'l'; 
    str_a[3] = 'l'; 
    str_a[4] = 'o'; 
    str_a[5] = ','; 
    str_a[6] = ' '; 
    str_a[7] = 'w'; 
    str_a[8] = 'o'; 
    str_a[9] = 'r'; 
    str_a[10] = 'l'; 
    str_a[11] = 'd'; 
    str_a[12] = '!'; 
    str_a[13] = '\n'; 
    str_a[14] = 0; 
    printf(str_a); 
} 

」前のプログラムでを、20要素の文字列は str_aとして定義され、配列の各要素は、1つずつ書き込まれます。数字が1ではなく0から始まることに注意してください。最後の文字が0 "

" (これはヌルバイトとも呼ばれます)文字配列が定義されているため、20バイトそのために割り当てられますが、実際に使用されるのは12バイトだけです。ヌルバイト最後のプログラミングは、文字列を処理している関数に、そこでの操作を停止するように指示する区切り文字として使用されます。残りの余分なバイトはガベージだけで無視されます。文字配列の5番目の要素にnullバイトが挿入されている場合、Helloのみの文字がprintf()関数 "によって出力されます。

" 文字配列の各文字を設定するのは面倒で、文字列はかなり頻繁に使用され、一連の標準関数が文字列操作用に作成されました。たとえば、strcpy()関数はソース文字列をコピーしてソース文字列を繰り返し、各バイトを宛先にコピーします(ヌル終了バイトをコピーした後に停止します)。

" 関数の引数の順序は、インテルのアセンブリー構文の宛先と最初に似ています。 char_array.cプログラムはstrcpy()を使って書き換えて、同じことを文字列ライブラリを使って行うことができます。それは文字列関数を使用しているため、以下に示すchar_arrayプログラムの次のバージョンは、」string.hのが含まれています。

#include <stdio.h> 
#include <string.h> 

int main() 
{ 
    char str_a[20]; 
    strcpy(str_a, "Hello, world!\n"); 
    printf(str_a); 
} 

C文字列の詳細情報を探す

http://www.cs.uic.edu /~jbell/CourseNotes/C_Programming/CharacterStrings.html

http://www.tutorialspoint.com/cprogramming/c_strings.htm

ポインタ

" EIPレジスタは、プログラムの実行中にメモリアドレスを格納して現在の命令を「指す」ポインタです。ポインターの考え方はCでも使われています。物理メモリーは実際には移動できないため、その中の情報をコピーする必要があります。異なる機能または異なる場所で使用される大きなメモリチャンクをコピーすることは、計算コストが高くなる可能性があります。ソースのコピーが可能になる前に、新しいコピー先の領域を保存または割り当てなければならないため、これはメモリの観点からも高価です。ポインタはこの問題の解決策です。大きなメモリブロックをコピーするのではなく、メモリブロックの先頭のアドレスを渡す方がはるかに簡単です。 "

" 他の可変型と同様に、ポインタを定義して使用できます。 x86アーキテクチャ上のメモリは32ビットのアドレッシングを使用するため、ポインタも32ビット(4バイト)です。ポインタは、変数名にアスタリスク(*)を付けることによって定義されます。その型の変数を定義する代わりに、ポインタはその型のデータを指すものとして定義されます。 pointer.cプログラムは、char型のデータ型で使用されているポインタの例です。サイズは1バイトで、サイズは "です。

#include <stdio.h> 
#include <string.h> 

int main() 
{ 
    char str_a[20]; // A 20-element character array 
    char *pointer; // A pointer, meant for a character array 
    char *pointer2; // And yet another one 
    strcpy(str_a, "Hello, world!\n"); 
    pointer = str_a; // Set the first pointer to the start of the array. 
    printf(pointer); 
    pointer2 = pointer + 2; // Set the second one 2 bytes further in. 
    printf(pointer2); // Print it. 
    strcpy(pointer2, "y you guys!\n"); // Copy into that spot. 
    printf(pointer); // Print again. 
} 

コード内のコメントが示すように、最初のポインタが文字列の先頭に設定されている。文字列は次のように参照されると、それが実際にポインタそのものである。これはどのようにこれですバッファは先にprintf()とstrcpy()関数へのポインタとして渡されました。第2ポインタは最初のポインタアドレス+2に設定され、次にいくつかのものが出力されます(下の出力に表示されます) "。

[email protected]:~/booksrc $ gcc -o pointer pointer.c 
[email protected]:~/booksrc $ ./pointer 
Hello, world! 
llo, world! 
Hey you guys! 
[email protected]:~/booksrc $ 

ポインタはメモリアドレスが含まれているので、オペレータのアドレスは、多くの場合、ポインタと一緒に使用される。addressof.cプログラムは、整数変数のアドレスを置くために使用されているオペレータアドレスの を実証 をポインターにします。この行は、太字の "で示されています。

#include <stdio.h> 

int main() 
{ 
    int int_var = 5; 
    int *int_ptr; 
    int_ptr = &int_var; // put the address of int_var into int_ptr 
} 

追加単項演算子が間接参照演算子は、ポインタを使用するために存在すると呼ばれる。この演算子は、代わりにアドレス自体の、ポインタが指しているアドレスで見つかったデータを返します。これは、の形式を取ります変数名の前にアスタリスク(ポインタの宣言に似ています)を返します。また、逆参照演算子はGDBとC "の両方に存在します。

"(addressof2.cに示す)addressof.cコードにいくつかの追加 これらの概念の全てを実証する。追加のprintf()関数は、私は、次のセクションで説明しますフォーマット パラメータを使用します今のところは、プログラム出力 "に注目してください。

#include <stdio.h> 

int main() 
{ 
    int int_var = 5; 
    int *int_ptr; 
    int_ptr = &int_var; // Put the address of int_var into int_ptr. 
    printf("int_ptr = 0x%08x\n", int_ptr); 
    printf("&int_ptr = 0x%08x\n", &int_ptr); 
    printf("*int_ptr = 0x%08x\n\n", *int_ptr); 
    printf("int_var is located at 0x%08x and contains %d\n", &int_var, int_var); 
    printf("int_ptr is located at 0x%08x, contains 0x%08x, and points to %d\n\n", &int_ptr, int_ptr, *int_ptr); 
} 

単項演算子はポインタを使用する場合間接参照演算子は、ポインタがを向いている方向に前方に移動しながら、オペレータのアドレスは、後退と考えることができます」。

コンピュータのメモリ上のポインタ&メモリ割り当て

教授ダン・ハーシュバーグ、コンピュータサイエンス学部、カリフォルニア大学の詳細をご覧くださいhttps://www.ics.uci.edu/~dan/class/165/notes /memory.html

http://cslibrary.stanford.edu/106/

http://www.programiz.com/c-programming/c-dynamic-memory-allocation

配列

はhttp://www.cprogramming.com/tutorial/c/lesson8.html

ここで利用可能なアレックスアラン名前CHAPによる多次元配列に単純なチュートリアルは名前CHAPによってアレイ上の情報をTheresのTheresのTodd A Gibsonはhttp://www.augustcouncil.com/~tgibson/tutorial/arrから入手できます。配列

配列対HTML

反復配列

#include <stdio.h> 

int main() 
{ 

    int i; 
    char char_array[5] = {'a', 'b', 'c', 'd', 'e'}; 
    int int_array[5] = {1, 2, 3, 4, 5}; 
    char *char_pointer; 
    int *int_pointer; 
    char_pointer = char_array; 
    int_pointer = int_array; 

    for(i=0; i < 5; i++) { // Iterate through the int array with the int_pointer. 
     printf("[integer pointer] points to %p, which contains the integer %d\n", int_pointer, *int_pointer); 
     int_pointer = int_pointer + 1; 
    } 

    for(i=0; i < 5; i++) { // Iterate through the char array with the char_pointer. 
     printf("[char pointer] points to %p, which contains the char '%c'\n", char_pointer, *char_pointer); 
     char_pointer = char_pointer + 1; 
    } 

} 

リンクリストは、リンクリストの情報を利用できる唯一のオプションではありません。この情報は私が私の研究を通して読んだものの一部を渡すために、単純に書かれた

http://www.eternallyconfuzzled.com/tuts/datastructures/jsw_tut_linklist.aspx

結論

他人を助けるかもしれない話題。