ポインターの適用に関する深い理解

1. ポインタの基本的な使い方


1. C プログラミングでポインターを使用する利点:

① プログラムをシンプル、コンパクト、効率的にすることができます。

②複雑なデータ構造の効果的な表現。

③動的メモリ割り当て。

④ 戻り値を複数取得する。


2. ポインタ変数の説明

コンピュータのメモリでは、各バイト単位にアドレスと呼ばれる番号があります。

1 バイト = 8 ビット 1 バイトは 8 ビットに等しい

C言語では、メモリ単位のアドレスをポインタと呼び、アドレスを格納するために特別に使用される変数をポインタ変数と呼びます。理解に支障のない場合、アドレス、ポインタ、ポインタ変数を区別せず、総称してポインタと呼ぶことがあります。

ポインター変数の一般的な形式: <ストレージ タイプ> <データ タイプ> *<変数ポインター>;

例: char *pName;

ポインターのストレージ型は、ポインター変数自体のデータ型です。

ポインタ宣言で指定するデータ型は、ポインタ変数自体のデータ型ではなく、ポインタ先のデータ型です。単にポインターと呼ばれるデータ型。


3. ポインタの初期化

ポインタを説明すると同時に、ポインタの初期化と呼ばれる初期値を与えることもできます。ポインタ代入演算とは, 代入演算子を介してアドレス値をポインタ変数に送信することを指します. 送信される値はアドレス定数またはポインタ変数である必要があり, 通常の整数 (0 を除く) は使用できません.

一般的な形式は次のとおりです。 <ストレージ タイプ> <データ タイプ> *<変数ポインタ>=<アドレス量>;

ポインター割り当ての一般的な形式:

①通常変数のアドレスを同じデータ型のポインタに代入する。

例如:int a=3; int *pa=&a;

int a,*pa; pa=&a;

② アドレス値が存在するポインタ変数を同じデータ型の別のポインタ変数に代入する。

例: float a,*px,*py;

px=&a;

py=px;

③配列のアドレスを同じデータ型のポインタに代入する。

例: int a[20],*pa;

pa=a; 或 pa=&a[0];

ポインタの指すメモリ領域のデータをポインタの対象と呼びます。指す領域がプログラム内の変数のメモリ空間である場合、この変数はポインタの対象変数と呼ばれます。ポインター オブジェクト


px をポインターとすると、次のようになります。

px—内容がアドレスであるポインター変数

*p - ポインターが指すオブジェクト。その内容はデータです。

&px——ポインタ変数が占有する記憶領域のアドレスは定数

第二に、ポインタ操作

1. 演算子の定義と使用

ポインタ演算は、ポインタ変数にオペランドとして格納されたアドレスに対して実行される演算です。

ポインター演算の本質は、アドレスの計算です。

ポインター演算の種類は限定されており、代入演算、算術演算、および関係演算のみを実行できます。

異なるデータ型の 2 つのポインターに対して整数演算の加算と減算を実行しても意味がありません。

オペレーター

計算フォーム

意義

+

PX+n

ポインタはアドレスが大きい方向に n データ移動します。

-

PX-n

ポインタはアドレスが小さい方向に n データ移動します。

++

PX++ または px++

ポインタはアドレスの大きい方へ1データ分移動

--

px-- または px--

ポインタはアドレスの小さい方へ1データ分移動

-

PX-PY

結果は、アドレスではなく、2 つのポインター間で区切られた

px+n で表される実際の場所のアドレスは、(px) + sizeof(px のタイプ)*n です。

px-n で表される実際の場所のアドレスは、(px) - sizeof(px のタイプ)*n です。

2. 演習 1: pq の例

#include <stdio. h>
int main(int argc, char *argv[])
{
int a[5]={4,8,1,2,7};        //p-q示例
int *P, * q;
p = a; //&a[0]; 
q = &a[3];
printf("%p %p\n", p, q);
printf("%d %d\n",*p,*q);
printf("%d\n", q-p);
return 0;
}
打印结果:
 0xbff5b7ac  oxbffb7b8
 4  2
 3

3. 演習 2: ポインターのインクリメントとデクリメントの例

#include <stdio.h>
int main(int argc,char *argv[]){
int a[5]= {4,8,1,2,7};
int *p,*q;
p= a; //&a[0];
printf("%p %d\n",p,*p);       //指针自增自减示例
q=p++;
printf("%p %d\n",p,*p);
printf("%p %d\n",q,*q);
return 0;
}
打印结果:
0xbfa66e4c   4
0xbfa66e50   8
0xbfa66e4c   4

3. 演習 3: 配列の入力と出力を使用する

2 つのポインター間の関係演算は、ポインターが指すアドレス位置間の関係を表します。大きなアドレスへのポインタは、小さなアドレスへのポインタよりも大きくなります。

ポインターと一般整数変数の間の関係演算は無意味です。ただし、ポインタが空かどうかを判断するために、ゼロを使用して等しいか等しくない関係演算を実行できます。

#include <stdio . h>
#define N 5
int main(int argc, char *argv[])
int a[N];
int *p, i;
p=a;//int*p=a;
for(i=6;i<N;i++)
scanf("%d" , p++);//&a[i]
p=a;
for(i=0;i<N;i++){
printf(" %d",*p++);  //++的优先级高,先p++,但是赋值还是之前的赋值给*p
}
puts(" " );
return 0;
}

3. ポインターと配列

1. 定義と使用

C言語では、配列のポインタはメモリ内の配列の先頭アドレスを参照し、配列要素のアドレスはメモリ内の配列要素の先頭アドレスを参照します。

一次元配列の配列名は、一次元配列のポインタ(開始アドレス)です。

例: double x[8]; したがって、x は x 配列の開始アドレスです。


设指针变量px的地址值等于数组指针x (即指针变量px指向数组的首元数), 则:

x[i]、*(px+i)、 *(x+i) 和px[i]具有完全相同的功能:访问数组第i+ 1个数组元素。

注意:指针变量和数组在访问数组中元素时,一定条件 下其使用方法具有相同的形式,因为指针变量和数组名都是地址量.但指针变量和数组的指针(或叫数组名)在本质上不同,指针变量是地址变量)而数组的指针是地址常量。

#include <stdio. h>
int main(int argc, char *argv[])
int a[]={1,6,9,12,61,12,21};
int *p,i, n;
p=a;
n = sizeof(a) / sizeof(int);
printf("%p %p %p\n", a, a+1, a+2);
for(i=0;i<n;i++)
printf("%d %d %d %d\n", a[i], *(p+i), *(a+i), p[i]);
puts(" ");
return 0;
}

2、练习一:编一个程序,实现数组中n个数的翻转。

#include <stdio.h>
int main(int argc, const char *argv[ ] )
{
int a[]={1,2,3,4,5,6};
int *p,*q,n,i, temp;
n=sizeof(a)/sizeof(int);
p=a;
q=&a[n-1];
while(p<q){
temp=*p;
*p=*q;
*q=temp;
p++ ;
q-- ;
}
for (i=0;i<n;i++){
printf( " %d\n",a[i]);
return 0;
}

四、指针与二维数组

多维数组就是具有两个或两个以上下标的数组。

在C语言中,二维数组的元素连续存储,按行优先存。

练习:编程实现,使用一级指针遍历二维数组。

#include <stdio. h>
{
int main(int argc, char *argv[])
int a[3][2] = {
    
    {1, 6}, {9, 12}, {61,12}};
int *p, i, n;
n = sizeof(a) / sizeof(int);
p =a[0]; //&a[0][0];
printf("%p %p\n", p, p+1);      一个一个加
printf("%p %p\n", a, a+1);      一行一行加
for(i=8;i<n;i++)
printf("%d,*(p+i));
puts("") ;
return 0;
}
输出结果:
0xbffeb188 0xbffeb18c
0xbffeb188 0xbffeb190
1 6 9 12 61 12

二维数组名代表数组的起始地址,数组名加1,是移动一行元素。因此,二维数组名常被称为行地址。

行指针(数组指针)

存储行地址的指针变量,叫做行指针变量。形式如下:

●<存储类型> < 数据类型> (* <指针变量名>)[表达式] ;

例如, int a[2][3]; int (*p)[3];

方括号中的常量表达式表示指针加1 , 移动几个数据。

当用行指针操作二维数组时,表达式般写成1行的元素个数,即列数。


1、练习1:编程实现使用行指针表示二维数组int a[3][2]的元素是 a[1][1]

#include <stdio . h>
int main(int argc,char *argv[])
{
int a[3][2] = {
    
    {1, 6},{9, 12},{61,12}};
int (*p)[2];
p=a;
printf("%p %p\n", a, a+1);
printf("%p %p\n", p, p+1);
printf( "%d, %d, %d, %d\n”, a[1][1], p[1][1], *(*(a + 1)+1), *(*(p + 1) +1);
return 0;      //a+1数组先移动一行,加*从行指针变为一级指针,+1再移动一列,加*再取值
}                               
输出结果:
0xbfb52e58 0xbfb52e60
0xbfb52e58 0xbfb52e60 
12,12,12,12

2、练习2:编程实现使用行指针遍历打印出二维数组int a[3][2]的元素

#include <stdio . h>
int main(int argc,char *argv[])
{
int a[3][2] = {
    
    {1, 6},{9, 12},{61,12}};
int (*p)[2];
p=a;
printf("%p %p\n", a, a+1);
printf("%p %p\n", p, p+1);
for(i=0;i<3;i++){
for(j=0;j<2;j++)
printf( "%d, %d, %d, %d\n”, a[i][j], p[i][j], *(*(a + i)+j), *(*(p + i) +j);
}
return 0;      //a+1数组先移动一行,加*从行指针变为一级指针,+1再移动一列,加*再取值
}                               
输出结果:

0xbfb52e58 0xbfb52e60
0xbfb52e58 0xbfb52e60 
1,1,1,1,6,6,6,6
9,9,9,9,12,12,12,12,
61,61,61,61,12,12,12,12

五、字符指针与字符串

1、定义与使用

C语言通过使用字符数组来处理字符串。通常,我们把char数据类型的指针变量称为字符指针变量。字符指针变量与字符数组有着密切关系,它也被用来处理字符事。

#include <stdio.h>
int main(int argc, char *argv[])
{
char ch='A';
char * p;
p = &ch;
printf("%C %c\n", ch, *p);
return 0;
}
输出结果:
A A

初始化字符指针是把内存中字符串的首地址赋予指针,并不是把该字符串复制到指针中

char str[] = "Hello World" ;

char *p =str;

在C编程中,当一个字符指针指向一个字符串常量时,不能修改指针指向的对象的值

例如:char *p="Hello World" ;

*p ='h' ;//错误,字符串常量不能修改

全局变量、static变量、字符串常量放在静态区,程序结束才会结束。

2、练习、不利用任何字符串函数,编程实现字符串连接函数的功能。

int main(int argc, char *argv[])
{
char ch[108] = "welcome" ;
char * p = "hello world!", * q;
int i=0;
q=p;              //保留一下p
while (*(ch+1) != '\0 ) 
i++;
while (*p != '\0') {
(ch+i) = *p;
i++;
p++;
}
*(ch+i) = *p;   //赋以'\0'
p=q;           //保留p
puts(ch);
puts(p);
return 0;
}
输出结果:
welcomehello World!
hello World!

六、指针数组

1、定义与使用

所谓指针数组是指由若干个具有相同存储类型和数据类型的指针变量构成的集合。

指针数组的一般说明形式:

<存储类型> <数据类型> *<指针数组名>[<大小>];

指针数组名表示该指针数组的起始地址.

#include <stdio.>
int main(int argc, char *argv[])
{
int * p[3];//int *....p[0] p[1] p[2]
int a[]={3,6,1,9,18};
p[0] = a;
p[1]=a+1;
p[2]=a+3;
printf( "%d %d %d\n", a[0], a[1], a[3]);
printf"%d %d %d\n",*(p[0]), *(p[1]), *(p[2]);  //[]的优先级大于*,所以()可省略
return 0;
}
输出结果:
3 6 9
3 6 9
#include <stdi0.h>
int main(int argc, char *argv[]) 
int a[2][3] = {
    
    {1, 4, 6},{12, 9, 7}};
int *p[2];
p[0]= a[0];//&a[0][0] 
p[1]= a[1];//&a[1][0]
printf("%d\n", a[0][1]);
printf( %d\n", *(a[0]+1));
printf( %d\n", *(p[0]+1));
return 0;
输出结果:
4
4
4

2、编程:利用指针数组处理一个二维数组 ,要求遍历二维数组所有元素。

#include <stdi0.h>
int main(int argc, char *argv[])
{
int a[2][3] = {
    
    {2, 4, 6},{12, 9, 7};//----a[0] a[1]
p[2] = {a[0], a[1]};
int 1, j;
//printf("%p %p\n", p[0],a[0]); 
//printf("%p %p\n", p[1],a[1]);
for(i=0;i<2;i++){
for(j=0;j<3;j++)
//printf("%d %d ",(a[i) + j),*(p[i]+j));
printf( "%d %d, *(*(a+i) + j),*(*(p+i)+j));
puts(" " ) ;
}
return0;
}

3、编程:利用指针数组处理一个二维数组 ,要求求出二维数组所有元素的和。

#include <stdi0.h>
int main(int argc, char *argv[])
{
int a[2][3] = {
    
    {2, 4, 6},{12, 9, 7};//----a[0] a[1]
p[2] = {a[0], a[1]};
int 1, j,sum=0;
//printf("%p %p\n", p[0],a[0]); 
//printf("%sp %p\n", p[1],a[1]);
for(i=0;i<2;i++){
for(j=0;j<3;j++)
//printf("%d %d ",(a[i) + j),*(p[i]+j));
sum+=*(p[i]+j);
printf( "sum=%d , sum);
puts(" " ) ;
}
return 0;
}
输出结果:sum=39

七、多级指针

1、定义与使用

把一个指向指针变量的指针变量,称为多级指针变量。

对于指向处理数据的指针变量称为一级指针变量,简称一级指针。

而把指向一级指针变量的指针变量称为二级指针变量,简称二级指针。

二级指针变量的说明形式如下

<存储类型> <数据类型> **<指针名> ;

#include <stdio. h>
int main(int argc, char *argv[])
{
int m=10;
int * p; 
int* * q;
p=&m;
q=&p;
printf("%p %p\n", p,&m);
printf("%p %p\n",q,&p);
printf("%d %d %d\n", m, *p, **q);
return 0;
}
输出结果:
0xbfde5884 0xbfde5884
0xbfde5888 0xbfde5888
10 10 10
#include <stdio. h>
int main(int argc, char *argv[])
{
int a[]={3,6,9);
int
p[2] = {&a[0], &a[1]};int ** q;
q = &p[0];//p 
q=p;
printf("%d %d\n", a[0], a[1]);
printf("%d %d\n", *p[0], *p[1]);
printf("%d %d %d\n", a[0], *p[0], **q);
printf("%d %d %d\n", a[1],*p[1], **(q+1));
return 0;
}

2、练习:指针数组的打印

#include <std1o. h>
int main(int argc, char *argv[])
{
//char s1 . "apple";
//char s2 = "pear";
char * s[] = {"apple", "pear”, "potato" }
int i, n;
i=0;
n = sizeof(s) / sizeof(char *);
p=&s[0];  //p=s;
while (i < n){
printf("%s %s\n", s[i],p[i]);
}
return 0;
}

七、void 指针和const修饰符

1、定义与使用

void指针是一种不确定 数据类型的指针变量,它可以通过强制类型转换让该变量指向任何数据类型的变量。一般形式为:void *<指针变量名称> ;

对于void指针,在没有强制类型转换之前,不能进行任何指针的算术运算

#include <stdio . h>
int main(int argc,char *argv[])
{
int m=10;
double n = 3.14;
void *p,*q;
p=&m;//p = (void * )&m;
printf("%d %d\n", m, *(int *)p);
q=&n;//q = (void *)&n;
printf("%.2lf %.2lf\n", n, *(double *)q);
return 0;
}
输出结果:  10  10
          3.14    3.14

2、练习:用void 指针遍历一维数组。

#include <stdio . h>        //方法一
{
int main(int argc, char *argv[])
inta[]={5,9,1,6,9,10};
int i, n;
void * p;
p=a;
n = sizeof(a) / sizeof(int);
for(1=0;1<n;1++)
printf("%d, *((int *)p + i);
puts("");
return 0 ;
}
#include <stdio . h>            //方法二
{
int main(int argc, char *argv[])
{
inta[]={5,9,1,6,9,10};
int i, n;
void *q;
int *p;
q= a;
p = (int *)q;
n = sizeof(a) / sizeof(int);
for(i=0;i<n;i++)
printf("%d”, *(p + i));
puts("");
return 0 ;
}

3、const

const变量:常量化变量的值

const <数据类型>变量名= [<表达式>];

常量化变量是为了使得变量的值不能修改。

变量有const修饰时,若想用指针间接访问变量,指针也要有const修饰。

常量化指针目标表达式

const <数据类型> * <指针变量名称>[= <指针运算表达式>];

常量化指针目标是限制通过指针改变其目标的数值, 但<指针变量>存储的地址值可以修改。

常量化指针变量

<数据类型> * const <指针变量名称>[= <指针运算表达式>];

使得<指针变量>存储的地址值不能修改。但可以通过* <指针变量名称>修改指针所指向变量的数值。

常量化指针变量及其目标表达式

const <数据类型> *const <指针变量名> = <指针运算表达式> ;

常量化指针变量及其目标表达式,使得既不可以修改<指针变量>的地址,也不可以通过* <指针变量名称>修改指针所指向变量的值。

おすすめ

転載: blog.csdn.net/qq_52049228/article/details/129646483