1. ポインタの基礎知識
ポインタの定義: ポインタの正式名はポインタ変数です。
ポインタは本質的には int、float、double、char と同じ型であり、この型をポインタ型と呼び、定義された変数をポインタ変数と呼びます。
int *p; この式は変数 p を定義することを意味し、* 記号は定義した変数 p がポインタ型であることを意味し、前の int は定義したポインタ変数 p が整数を指し、ポインタ変数を意味します。 p の値は、p が指す整変数のアドレスであるため、ポインタに値を代入する場合、それはアドレス値になります。
&: アドレス記号を取得し、変数の前に & を追加すると、全体が変数のアドレスを表します
*: 変数定義で*を使用した場合はポインタ型変数を定義することを意味し、それ以外の場合はポインタ変数が指す変数アドレスに格納されているデータを操作することを意味します。たとえば、int a = 5; int *p; *p =& a
int a = 5; 整変数 a が定義され、a には最初に値 5 が割り当てられます。
p = &a; a は整数変数で、a の前に & を追加してアドレス記号を取得し、&a 全体が変数 a のアドレスを表し、このアドレスを割り当てます ポインタ変数 p を与えます。つまり、ポインタ変数 p の値は、ポインタ変数 p が指す int 変数 a です。
2. ポインタ変数の操作
ポインタは本質的には変数ですが、この変数はポインタ型であり、int型などの変数型と本質的な違いはありません。変数なので、+ - * / % の算術演算が可能です。ポインタ型変数の値がアドレス * / % などであるというだけです。これらの操作は可能ですが、実用的な意味はありません。最も一般的な使用法は、ポインターの + - 演算です。配列値の操作方法と組み合わせると、最も明白になります。
int a[4] = {100,200,300,400}; 整数配列を定義します。この配列には 5 つの整数型データがあります。
int *p; 整数変数を指すポインタ変数 p を定義します。p の値は、指す整数変数のアドレスです。
p = a; 配列名が右辺値の場合、配列名が配列の最初の要素のアドレス、つまり a[0] のアドレスであることを意味します。a と &a[0] は両方とも同等です。配列要素の最初のアドレスを示します
pはa[0]の先頭アドレスを指します *pの値はa[0]です
p+1 は a[1] のアドレスを指します *(p+1) の値は a[1] です
p+2 は a[2] のアドレスを指します *(p+2) の値は a[2] です
*(p+1) の場合、() が最も優先されるため、() の式が最初に計算されます。p+1 は、p が指す変数のアドレスに対して +1 演算を実行することを意味します。ここでは +1 は実行されませんアドレス 値は +1 ですが、アドレスは 4 増加します。この 1 の意味は、p が指す変数の型のサイズを増やすことです。p が指す変数は整数型であり、そのサイズは整数型はセクションのデータサイズが 4 ワードを占めるため、4 が加算されます。次の表を例として取り上げます。
*p=a[0] *(p+1)=a[1] *(p+2)=a[2] *(p+3)=a[3]
a[0] =100 | a[1]=200 | a[2]=300 | a[3]=400 |
p p+1 p+2 p+3
上の表から、p=&a[0]; &a[0] のアドレス値が 0x1000 である場合、p の値は 0x1000 であることがわかります。p+1 の値は 0x1001 ではなく 0x1004 であるため、この +1 の値は実際にはメモリ内で int が占める長さ (4 バイト) になります。
p = &a[0];
printf("*p=%d\n",*p); *p の値は、p が指す変数のアドレスに格納されている値です。p が指す変数は a[0]、p が指す変数のアドレスは 0x1000、アドレス 0x1000 に格納されている値は 100 であるため、*p = 100;
*p = 200; これは、p が指す変数のアドレスに格納されている値を変更することと同じです、つまり、0x1000 に格納されている 100 の値が 200 に変更されます、つまり、a[0] の値は200。
*(p+1) と *p は同じ意味です。まず、()内が最も優先度が高いことを決定し、pはポインタ変数、pの値はpが指す変数のアドレス、pが指す変数はa[0]、そしてa[0] のアドレスは 0x1000 なので、 p の値は 0x1000 です。 p+1 は、元の基準に sizeof(int) バイトを追加した p の値 (4 バイト) なので、 p+1=0x1004、つまり、p+1 の値 &a[1] なので、p+1 は整数変数 a[1] を指し、p+1 の値は a[1] のアドレスになります。※演算式の意味はポインタが指す変数のアドレスの値を演算することであり、今p+1は整数変数a[1]を指しているので、
*(p+1) = a[1]、つまり *(p+1) = 200; 次の演算は類推により推測できます。
3. ポインタ型のキャスト
int a = 0x11223344;
char *p;
p = &a;
上記のコードを分析します。ポインタ変数 p が char 型の変数を指しており、ポインタ変数に値を代入する際、p が指す変数 a の型が int 型であるため、この代入方法は不合理であり、コンパイラは警告またはエラーを報告します。実際にこの方法を使用してプロジェクトに値を割り当てると、予期しないリスクが発生します。ポインターが整数変数を指すように、ポインター変数 p のみを再定義してください。次のように
int *p;
p = &a;
この場合、*p の値は 0x11223344 になります。
冒頭の方法に従って定義を追加しましたが、0x11、0x22、0x33、0x44の4つの値を個別に取り出す必要があるのですが、どのように操作すればよいでしょうか?これはポインタ型の強制変換を伴います。次のように実行します。
int a = 0x11223344;
char *p;
p=(char *)&a;
分析: () が最も優先度が高いため、最初の操作は () 内から開始されます。char * はポインタが指す変数の型を char 型にキャストすることを意味します。ポインタ p が指す変数は a、 a は int 型であり、int 型は char 型にキャストされます。ポインタ変数 p 自体の値、または a&a のアドレス値は変更されません。唯一変わるのは、ポインタ変数 p が指す変数 a の型がキャストされることです。*p の値は何ですか?
強制変換後、p は char 型の変数を指すため、*p の値は char 型の値になります。次の表を使って分析します。
0x11 | 0x22 | 0x33 | 0x44 |
0x1000 0x1001 0x1002 0x1003
p p+1 p+2 p+3
p = (char*)&a; p的值还是a的地址,就是p的值是0x1000;但是p所指向的变量也就是a的类型强制转换为char型,a在内存中还是按照整型数据存储的,但我们取值的时候是按照char型取出的。
说以*p取值时取的是一个char型变量,也就是0x11,
*(p+1) 这里+1的意思是地址+sizeof(char)个字节,不是+sizeof(int)个字节,因为我们在指针赋值时强制将指针所指向的变量类型转换为char型,所以p+1的值是0x1000+1就是0x1001,而0x1001的地址存放的值是0x22,所以
*(p+1) = 0x22;
*(p+2) = 0x33;
*(p+3) = 0x44;
这种操作一般用在函数传参时的类型强制转换,一般用在内存读写上。
注:一定要明白指针类型的强制转换的含义,强制转换的是指针所指向的变量类型,而不是指针类型,因为指针本来就是一个类型了。对指针进行+ -操作时,每次+1的值含义是地址偏移sizeof(char)字节,而不是sizeof(int)字节。
如何将一个有意义的整型变量转换为一个地址?
char *p;
int a = 0x1200;
p = (char* )a;
分析:将a强制转换为指针类型,则a的值由一个int型的值转换为一个地址值了,这个p指针变量指向char型变量。
举例:
void ReadFlash(uint32_t addr, uint8_t *ptr, int len)
{
int i;
for(i = 0; i < len; i++)
{
*ptr++ = (char *)addr++; //将整型addr的值赋值给ptr指针,则ptr的地址值就是addr的值,ptr所指向的变量
//char型的,也就是ptr取值时是按照char数据类型大小来取值的。
}
}
#define BUFF_LEN (100)
int main(void)
{
uint8_t buff[BUFF_LEN] = {0};
ReadFlash(0x10000,buff, BUFF_LEN);
}
整型变量强转为地址值时用的很少,但在内存中取值时这种用法有时候是必须的。
4.函数传参的俩种方式,传值和传地址
调用函数时经常要给函数的形参列表来传递参数,将实参传递给形参有俩种方法
第一种:传值
调用函数时将实参的值传递给形参,实际上是将实参的值拷贝一份传递给形参,而实参并没有参与调用函数的运算,所以传值传参是不会改变实参的值的,因为是将实参值拷贝了一份传给形参。
第二种:传地址
实参向形参传地址,则形参是指针类型的,将地址赋值给形参,则形参和实参共用同一片内存地址,也就是说通过传进来的地址对形参进行操作,会改变实参的值,因为形参和实参的地址是同一个,通过指针操作修改形参值,也就修改了实参的值。