2009-03-03 4 views
8

문자열 배열에서 단일 문자를 처리하는 방법을 이해하려고합니다. 또한 이것은 물론 포인터 첨자에 대한 일반적인 이해를 돕습니다. char **a이 있고 두 번째 문자열의 세 번째 문자에 도달하려면이 작업을 수행하십시오 : **((a+1)+2)? 그것이 해야하는 것처럼 보입니다. ...C의 문자열 배열에서 개별 문자에 어떻게 액세스합니까?

답변

19

거의 비슷합니다. 정답은 :

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

먼저 실제 문자열 포인터 중 하나 참조 해제 한 다음 문자열이 원하는 문자까지 포인터를 선택한 참조를 해제 할 필요가 있기 때문이다. (거기에 작업 순서를 명확하게하기 위해 괄호를 추가했습니다.)

또는이 표현 :

a[1][2] 

당신이 뭘 하려는지의 의도보다 자기 분명하고 표기 자체가 더 간결하기 때문에 또한 .... 작동 아마도 선호 될 것입니다! . 이 형식은 언어를 처음 접하는 사람들에게는 즉시 명백하지 않을 수 있지만, 배열 표기법이 작동하는 이유는 C에서 배열 색인 작업이 실제로는 동등한 포인터 작업에 대한 단축형이기 때문입니다. 즉 : * (a + x)는 a [x]와 같습니다. 따라서이 논리를 원래의 질문으로 확장하면 두 개의 별도의 포인터 역 참조 연산이 함께 계단식으로 연결되어 식 a [x] [y]가 일반 형식 * ((* (a + x)) + 와이).

+0

포인터에 대한 포인터를 다른 선언없이 배열로 사용할 수 있습니까? – sdellysse

+0

이유를 설명하면 +1하겠습니다. –

+0

장면 뒤에서는 [a]가 * (a + b)로 변환됩니다. –

1

시도 a[1][2]. 또는 *(*(a+1)+2).

기본적으로 배열 참조는 포인터 역 참조를위한 구문 설탕입니다. a [2]는 a + 2와 같고 2 [a]와 동일합니다 (읽을 수없는 코드를 원한다면). 문자열 배열은 이중 포인터와 같습니다. 따라서 [1] 또는 *(a+1)을 사용하여 두 번째 문자열을 추출 할 수 있습니다. b (2) 또는 *(b + 2) 중 하나를 사용하여 해당 문자열에서 세 번째 문자를 찾을 수 있습니다 (지금은 'b'라고 부름). 원래의 두 번째 문자열을 'b'로 대체하면 [1] [2] 또는 *(*(a+1)+2)이됩니다.

2

IIRC는 문자열은 실제로 문자의 배열이기 때문에이 작동합니다 :

a[1][2] 
3

당신은 포인터를 사용할 필요가 없습니다.

int 주 (int argc, 문자 ** ARGV) {

의 printf (argv를 "[1] ARGV의 세 번째 문자는 [%의 (C)]. \ n은"[1] [2 ]));

} 그런

:

$ ./main 헬로 [1] ARGV의 세 번째 문자는 [1,5]이다.

그건 1과 1입니다.

원하는 경우 포인터를 사용할 수 있습니다 ...

(*는 argv [1] +2)

또는

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

로서 사람 위에 지적했다.

이것은 배열 이름이 포인터이기 때문입니다. C 포인터의 위키 article에서

+0

펜던 트리 : argv [1] [3] argv [1]의 네 번째 문자 ... – 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 Explres of exploitation의 해킹 제 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/

메모리

" 컴파일 된 프로그램 메모리는 다섯 개의 세그먼트로 분할된다. 텍스트, 데이터, BSS, 힙, 및 스택의 각 세그먼트는 메모리의 특정 부분을 나타낸다 텍스트 세그먼트는 코드 세그먼트라고도하며, 프로그램의 어셈블 된 기계어 명령어가있는 곳입니다. "입니다.

" 세그먼트 내의 명령어의 실행, 컴파일 브랜치 점프에 및 어셈블리 언어 명령어를 호출 상기 하이 레벨의 제어 구조 및 기능 덕택 비선형이다. 프로그램으로서 실행하는 상기 EIP는 텍스트 세그먼트의 첫 번째 명령으로 설정된다 프로세서는 다음을 수행하는 실행 루프 다음과 같다. "

는" 1. EIP는 "

"가리키는 지시를 읽 2는 명령의 바이트 길이는 "

는" 3. 제 1 "

단계로 되돌아 간다 1"

"단계에서 판독 한 명령을 실행을 EIP에 추가

" 경우에 따라 명령이 점프 또는 통화 명령어가되며 은 EIP를 다른 메모리 주소로 변경합니다. 프로세서는 실행이 비선형 일 것으로 예상하기 때문에 변경에 대해 신경 쓰지 않습니다. 3 단계에서 EIP가 변경되면 프로세서는 1 단계로 돌아가서 변경된 EIP 주소의 설명을 "으로 읽습니다.

" 쓰기 권한이 텍스트 세그먼트에서 다음과 같이 비활성화됩니다. 변수를 저장하는 데 사용되지 않으며 코드 만 사용됩니다. 이렇게하면 사람들이 프로그램 코드를 실제로 수정하는 것을 방지 할 수 있습니다. 이 메모리 세그먼트에 쓰기를 시도하면 프로그램은 사용자에게 어떤 일이 발생했음을 알리고 프로그램 이 종료됩니다. 읽기 전용 인이 세그먼트의 또 다른 장점은 프로그램의 여러 복사본간에 을 공유 할 수 있으므로 문제없이 동시에 번 프로그램을 실행할 수 있습니다. 이 메모리 세그먼트 것도 있기 때문에, 그 어느 변화의 크기가 고정되어 있음을 유의해야한다 그것을 ".

" 데이터 및 BSS 세그먼트가 전역 및 정적 프로그램을 변수를 저장하는데 사용된다. 데이터 세그먼트는 초기화 된 전역 변수와 정적 변수로 채워지는 반면 bss 세그먼트는 초기화되지 않은 부분으로 채워집니다. 이러한 세그먼트는 쓰기 가능하지만 고정 크기도 있습니다. 앞의 예제에서 변수 j와 같은 함수 컨텍스트에도 불구하고 전역 변수가 유지된다는 것을 기억하십시오. 그들은 자신의 메모리 세그먼트에 저장되기 때문에 모두 전역 및 정적 변수는 지속 할 수 있습니다. "

는" 힙 세그먼트는 프로그래머가 할 수있는 직접 제어 메모리의 세그먼트입니다. 이 세그먼트의 메모리 블록은 프로그래머가 필요로하는 것이 무엇이든지간에 에 할당되고 사용될 수 있습니다.힙 세그먼트에 대한 하나 주목할만한 점은없는 고정 된 크기 때문이다, 그래서을 필요에 따라 크거나 작은 성장할 수 있습니다. "

" 힙에서 메모리의 모든 할당 및 역할 당기 알고리즘에 의해 관리됩니다 이들은 각각 힙에서 사용하기 위해 메모리 영역을 예약하고 예약을 제거하여 메모리의 해당 부분을 나중에 예약 할 수있게합니다. 힙은 얼마나 많은 메모리가 사용을 위해 예약되어 있는지에 따라 증가하고 줄어들 것입니다. 즉, 힙 할당 함수를 사용하는 프로그래머는 즉석에서 메모리를 예약하고 해제 할 수 있습니다. 힙 하방으로 높은 메모리 향해 이동 의 성장을 다룬다. "

" 스택 세그먼트는 가변 크기를 가지며, 함수 호출시에 로컬 함수 변수와 문맥을 저장하는 임시 스크래치 패드로서 사용된다. 이것은 GDB의 backtrace 명령이 보는 것입니다. 프로그램이 함수를 호출하면 해당 함수는 전달 된 변수 집합을 가지며 함수의 코드는 텍스트 또는 코드 세그먼트의 다른 메모리 위치에 있습니다. 함수가 호출 될 때 컨텍스트와 EIP가 변경되어야하므로 스택은 전달 된 모든 변수, 함수가 끝난 후에 EIP가 반환해야하는 위치 및 해당 함수가 사용하는 모든 로컬 변수를 기억하는 데 사용됩니다. 이 모든 정보는 집합 적으로 스택 프레임이라고하는 스택에 함께 저장됩니다. 스택에는 많은 스택 프레임이 포함됩니다. "

" 일반적으로 컴퓨터 과학 용어에서 스택은 자주 사용되는 추상 데이터 구조입니다. 그것은 선입 선출 (FILO) 주문 을 가지고 있습니다. 즉, 스택에 놓인 첫 번째 항목은 마지막 항목입니다. 한 쪽 끝에 매듭이있는 끈에 구슬을 놓는 것으로 생각하면 다른 모든 구슬을 제거 할 때까지 첫 번째 구슬을 꺼낼 수 없습니다. 항목이 스택에 배치되면 푸시라고하며 스택에서 항목을 제거하면이를 팝업으로 표시합니다.

" 이름에서 알 수 있듯이 메모리의 스택 세그먼트는 실제로 , 스택 프레임을 포함하는 스택 데이터 구조. ESP 레지스터는 스택 끝의 주소를 추적하는 데 사용됩니다. 스택의 주소는 항목이 푸시되고 푸시됨에 따라 계속 변경됩니다. 이것은 매우 동적 인 동작이기 때문에 스택도 고정 된 크기가 아님을 의미합니다. 힙의 동적 성장 반대 스택 변화 크기 S, 그것은을 해결 낮은 메모리 향해 메모리 시각적 목록에서 위로 성장한다. "

"스택 FILO 자연 홀수 보일 수도 , 스택은 문맥을 저장하기 위해 으로 사용되기 때문에 매우 유용합니다. 함수가 호출되면 스택 프레임에 여러 항목이 함께 스택에 푸시됩니다. EBP 레지스터 (때때로 프레임 포인터 (FP) 또는 로컬베이스 (LB) 포인터라고도 함) - 현재 스택 프레임의 로컬 함수 변수를 참조하는 데 사용됩니다. 각 스택 프레임에는 함수에 대한 매개 변수, 로컬 변수 및 저장된 포인터 (SFP) 및 복귀 주소 등을 되돌리기 위해 필요한 두 개의 포인터가 포함됩니다. 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이 아니라 0에서 시작됩니다. 또한 마지막 문자는 0 "

" (null 바이트라고도 함)입니다. 문자 배열이 정의되어 있으므로 20 바이트 그것에 대해 할당되지만 실제로 12 바이트 만 사용됩니다. null 바이트 프로그래밍은 끝에있는 작업을 중지 할 문자열을 처리하는 모든 함수에 알려주기 위해 구분 문자로 사용됩니다. 나머지 여분의 바이트는 그냥 쓰레기이며 무시됩니다. 문자 배열의 다섯 번째 요소에 null 바이트가 삽입되면 Hello 문자 만 printf() 함수 "에 의해 인쇄됩니다.

" 문자 배열의 각 문자를 설정하는 것은 번거롭고 문자열은 상당히 자주 사용되었지만 문자열 조작을위한 일련의 표준 함수가 만들어졌습니다. 예를 들어 strcpy() 함수는 문자열을 소스에서 대상으로 복사하고 소스 문자열을 반복하며 각 바이트를 대상에 복사합니다 (null 종결 바이트를 복사 한 후 중지).

" 함수 인수의 순서는 먼저 Intel 어셈블리 구문 대상과 유사하고 소스와 비슷합니다. 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에서 사용됩니다. 실제 메모리는 실제로 이동할 수 없기 때문에 실제 메모리를 복사해야합니다. 서로 다른 기능이나 다른 장소에서 사용하기 위해 많은 양의 메모리를 복사하는 것은 계산 상 많은 비용이 소요될 수 있습니다. 이는 원본 복사본을 복사하기 전에 새 대상 복사본의 공간을 저장하거나 할당해야하기 때문에 메모리 관점에서 볼 때 비용이 많이 듭니다. 포인터는이 문제에 대한 해결책입니다. 큰 메모리 블록을 복사하는 대신 그 메모리 블록의 시작 주소를 전달하는 것이 훨씬 더 간단합니다. "

" 다른 변수 유형과 마찬가지로 포인터를 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. 
} 

" 첫 번째 포인터가 문자 배열의 시작 부분에 설정되어 나타냅니다. 그것은 포인터 자체는 사실이다.이 방법이 있습니다 buffer는 이전에 printf()와 strcpy() 함수에 대한 포인터로 넘겨졌고, 두번째 포인터는 첫 번째 포인터 주소에 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

알렉스 Allain이 가능라는 이름의 갈라진 금에 의해 다차원 배열에 대한 간단한 튜토리얼을 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

나는 내 연구를 통해 읽은 내용이 정보의 일부를 전달하기 위해 간단하게 작성되었습니다

결론 다른 사람들을 도울 수있는 주제.

관련 문제