c语言各个数据类型取值范围?指针变量指向指针(地址),如何解引用二进制序列?需要确定这个二进制序列的长度以及解码规则,这也就是指向的数据类型由此,指针的算术运算也与其指向的类型密切相关,现在小编就来说说关于c语言各个数据类型取值范围?下面内容希望能帮助到你,我们来一起看看吧!
指针变量指向指针(地址),如何解引用二进制序列?需要确定这个二进制序列的长度以及解码规则,这也就是指向的数据类型。由此,指针的算术运算也与其指向的类型密切相关。
指针变量的算术运算主要有两类:
I 地址偏移
II 两个地址之间的差值
电子计算机以字节为单位进行编址,指针变量的算术运算以指针(地址)为运算对象,但并不单纯以字节为计算单位,而是以其指向的数据类型的大小为单位进行偏移或计算差值。
还可以从表达式的类型一致来理解,在C语言中,表达式的类型需要一致,但指针的算术运算如何达成一致?指针是地址,是一个整数,当然是一个合法地址,且具有类型信息,由此,与其运算的整数也不再是一个单纯的整数,而是具有类型信息的。
记住指针的偏移或算术运算的单位是元素的个数而不是byte数量,在计算新地址时千万别弄错了。
1 地址偏移p±n == (char*)p ± n*sizeof(*p)
void ptrCal()
{
int a[10],*p,*q;
for(int i=0;i<10;i )
a[i] = i;
p = a;
q = a 2;
int b = a[q-p];
printf("%d\n",b); // 2
b = (int)q - (int)p;
printf("%d\n",b); // 8
}
p-q = ±n
如果指针变量指向的类型是char类型,则指向类型的大小与单个字节的长度完全统一,两者具有天然的同步性。
int strLen(const char* strP)
{
const char* p = strP;
while(*p != '\0');
return p-strP-1;
}
除了在声明中或者当一个数组名是sizeof运算符或&运算符的操作数之外,编译器总是把数组名解释成指向它的第一个元素的指针。可以将这个原则表达为:
arr == &a[0] 或者
arr == &*a
n维数组arr,基首元素的地址是n-1维的&a[0]
int arr[2][3][4];
int (*p)[3][4] = &arr[0];
int (*q)[3][4] = &*arr;
int (*r)[3][4] = arr;
void arrPtr()
{
int a[3];
int b[3][4];
int c[3][4][5];
int i = sizeof a / sizeof *a; // 运算符sizeof的上下文,数组名表示整个数组
int j = sizeof b / sizeof *b;
int k = sizeof c / sizeof *c;
printf("%d,%d,%d\n",i,j,k); // 3,3,3
int(*pp)[3] = &a 1; //运算符&的上下文,数组名表示整个数组
int(*qq)[3][4] = &b 1;
int(*rr)[3][4][5] = &c 1;
int l = (int)pp-(int)a;
int m = (int)qq-(int)b;
int n = (int)rr-(int)c;
printf("%d,%d,%d\n",l,m,n); // 12,48,240
int *p = a; //以上两种情况以外的上下文,数组名表示数组首元素的地址
int (*q)[4] = b;
int (*r)[4][5] = c;
*(p 2) = 3;
*(*(q 2) 3) = 3*4;
*(*(*(r 2) 3) 4) = 3*4*5; //
printf("%d,%d,%d\n",a[2],b[2][3],c[2][3][4]);// 3,12,60
// 在数组名表示数组首元素的地址,数组名 n表示数组元素的偏移,
// 其偏移的字节数自然是数组元素的长度,n维数组的元素是n-1维数组,
// n-1维都需要有确定的长度信息
}
对于二维数组
arr[N][M]
其元素
arr[i][j]
便是指针写法
*(*(arr i) j)
的语法糖。在指针写法的表达式中,其偏移有意义的前提就是arr必须转换为指向数组首元素的指针。
对指针进行加1 操作,得到的是下一个元素的地址,而不是原有地址值直接加1。所以,一个类型为 T 的指针的移动,以sizeof(T) 为移动单位。如果有数组arr:
arr 1 代表是的数组首元素的地址即a[0]的首地址。
&a 1 代表的是数组的首地址,
两者指向的地址相同,但类型及代表的长度不同,所以有不同的移动。
单重循环处理二维数组:
#include <stdio.h>
int main()
{
const int M = 3;
const int N = 4;
int arr[M][N] = {0};
for(int i=0;i<M*N;i )
arr[i/N][i%N] = i; // 单重循环处理二维数组
int (*pArr)[N] = arr;
for(i=0;i<M*N;i ){
if(i%N==0)
printf("\n");
printf("%d ",*(*(pArr i/N) i%N));
}
printf("\n");
for(i=0;i<M;i ){
for(int j=0;j<N;j )
printf("%d ",arr[i][j]);
printf("\n");
}
getchar();
return 0;
}
结构体成员的偏移:
结构体成员的字节对齐,有利于提高数据的访问速度(CPU一次读取一个字长(32位平台是4个字节)的字节数),是典型的以空间换时间的案例。
结构体类型变量相对于结构体成员,如果数组名之于数组成员一样,提供的是一个基准地址,成员地址是相对于基准的偏移。不同的是,数组名是一个常量,不能用做左值(可以在声明的同时初始化),结构体变量是可以做左值的。(依序器如此规定也有其合理性,数组是数组名 索引确定分量地址,而结构体是由结构体变量 结构体分量确定分量地址,数组名做为分量的索引的基准需要保持不变)
#include <stdio.h>
#include <iostream>
void structMemberOffset()
{
struct Student
{
int grade;
char id[11];
char name[20];
char sex;
double chinese;
double math;
};
printf("%d\n",sizeof Student); //56
printf("%d\n",offsetof(Student,chinese)); // 40 = 4 12 20 4
Student stu;
int n = int(&stu.chinese)-int(&stu); // 40
printf("%d\n",n);
}
int main()
{
structMemberOffset();
return 0;
}
-End-
,