本博客大量参考 《c primer plus》 ,希望以后复习舒服些
前言
在c/c++语言中,我们都是用过数组和指针,但一开始的时候肯定是不怎么顺手的,会犯很多错误,最难使用的还是c语言的内置类型数组和内置类型指针,由于c++出现的较晚,吸收了c语言的精华,又有自己的新家伙,比如动态数组vector,智能指针等。但是我们同样需要了解最基本的,才会尽最大可能使用c++的工具时不会出现太大错误。 本篇博客依据c语言的内置类型数组和内置类型指针来叙述。
数组(数组的下标从零开始 )
数组 : 由数据类型相同的一系列元素组成。在使用数组时,要通过声明告诉编译器数组中有多少个元素和元素的类型是什么。
我们通过代码看看数组的声明和初始化:
#include<stdio.h>
int main(){
int array[5];
float array_1[2];
int apple[6]={1,2,3,4,5,6};
int arr[5]={1,2,3};
int ar[6]={[5]=1};
return 0;
}
c语言的数组要求严格,不允许数组间的赋值,但是c编译器不会检查数组数组越界情况和数组索引的正确性
int arr[3]; int app[4]; app=arr;// [Error] assignment to expression with array type
数组下标越界可能会导致程序崩溃,使用数组不越界是程序员的责任
数组的[ ] 的值要求是整形常量表达式 类型与 1 这个就是 整形常量,sizeof表达式被视为整形常量 并且在 [ ]内部的值必须要大于0
int array[sizeof(int)]
int arr[-1]
int arr[0]
int arr[1.2]
int arr[(int)1.2]
c 99 允许了一种数组的声明,叫 变长数组(VLA)
int a=3;
int arr[a]
这种数组,先不介绍了。
当我们不想改变数组里的元素时,我们可以使用const限定符,来表示该数组只读,不准修改里面的变量。
#include <stdio.h>
int main()
{
const int days[] = {31,28,31,30,31,30,31,31,30,31};
int index;
for (index = 0; index < sizeof days / sizeof days[0]; index++)
printf("Month %2d has %d days.\n", index +1,
days[index]);
days[1]=29;
return 0;
}
现在我们要重复刚才的几句重要的话,相当重要的话: 1,数组具有容器的特性 ,数组存放相同的数据类型 2,数组的大小必须在编译时给出来 3,数组中的元素在内存中是连续排列的,因此支持随机访问 4,用数组时放在[]中的数字叫做下标,数组的下标从零开始,下标必须大于0 5,数组下标越界可能会导致程序崩溃,使用数组不越界是程序员的责任 6,大多数的时候数组名代表数组开始的地址(有两个特例)(马上讲指针的时候要讲到
指针(内置类型)
指针是一个值为内存地址的变量 那指针既然是变量,变量肯定是可以被赋予值的,是的,可以,赋予指针的值就是其他变量的物理地址,我们通过使用 &运算符 得到一个变量的物理地址
int* ptr=NULL;
int a=3;
ptr=3;
ptr=&3;
int b=*ptr;
好了,我们现在已经会了指针的基本用法了,指针代表操控数据的地址就等于操纵数据,大家多多练习。 我们将通过指针和数组来继续探讨它们更高级的用法。 下面我们来看看指针和数组
指针和数组
这里大家要记住一句话,数组的名字代表地址,它代表数组开始的地址, 来看图片: 所以,我们可以使用指针来操作数组!
float array[5]={1,2,3,4,5}
float* ptr=array;
for(size_t i=0;i<5;i++){
printf("%p",*(array+i));
}
我们现在给出书中的一个代码,更为详细: 代码来源(c primer plus)
#include <stdio.h>
#define SIZE 4
int main(void)
{
short dates [SIZE];
short * pti;
short index;
double bills[SIZE];
double * ptf;
pti = dates;
ptf = bills;
printf("%23s %15s\n", "short", "double");
for (index = 0; index < SIZE; index ++)
printf("pointers + %d: %10p %10p\n",
index, pti + index, ptf + index);
return 0;
}
在我的系统电脑里,short占用2个字节,double占用8个字节,以下为gcc 编译器执行后的exe, 我们需要记住一句话:指针加1,递增的是它所指向类型的大小。 接下来博主就直接写出指针的用法总结了(内置类型的指针都包含的特点,而c++ 将迭代器(广义指针)设置了不同的等级,具体的了解可以去百度) 1,赋值:指针之间可以直接赋值,但要注意等号两边类型的兼容性,(c 语言的类型检查没有c++严格,c语言可以将数字赋予指针,但c++不行)
int* ptr1=& 1;//ptr1 是一个int* 类型的指针变量,里面存的是1的地址 int* ptr2; //ptr2 是一个int* 类型的指针变量 ptr2=ptr1;//指针间的赋值,将ptr1变量里的内容(地址)赋给ptr2 来看张图片 (这个博主也不咋明白的) 这种内置类型的指针指向栈区和静态存储区,赋值还是不容易出错的,但当内置类型的指针指向堆区存储时,就会出现很多问题,比如: 如图中我们所看到的,两个指针指向同一个内容,若释放内存时,极易发生操作失误,这就是深拷贝和浅拷贝的问题,大家可以自行去百度。)
2,解引用操作:我们可以使用 * 这个运算符来获得指针变量存储的内容 3,取地址:由于指针也是变量,那变量肯定会在内存有地址的,我们可以对指针是用 & ,来获得指针的地址。 4,指针和整数进行相加:这样获得的也是指针,只不过是修改了指针的步长,使用指针处理数组时经常使用 5,递增指针:当指针指向数组时,递增指针,如前面说的: 指针递增,递增的是它所指向类型的大小 6,指针减去一个数 7,递减指针 8,指针求差:两个指针可以相减的前提是:这两个指针指向同类型数组,我们得到的是距离 9,进行比较:两个指针可以比较的前提是:这两个指针指向同类型数组 10,用作循环的判断条件:我们可以在一些循环表达式中使用,进而判断 以上,大家无需用口记忆,多多练习即可
接下来我们将几个重要的公式:
int array[5]={1,2,3,4,5};
int* ptr=arrar;
*(array+2)=array[2];
array+2=&array[2]
多多练习,自然就记住了。
指针和递增运算符和解引用运算符联合使用 有时候,我们在看一些高手写的代码时,他们经常会把几步骤的代码写成一行,比如:
*p++ *++p (*p)++ 这该如何理解你?
我们先看书中的代码把:
#include <stdio.h>
int data[2] = {100, 200};
int moredata[2] = {300, 400};
int main(void)
{
int * p1, * p2, * p3;
p1 = p2 = data;
p3 = moredata;
printf(" *p1 = %d, *p2 = %d, *p3 = %d\n",
*p1 , *p2 , *p3);
printf("*p1++ = %d, *++p2 = %d, (*p3)++ = %d\n",
*p1++ , *++p2 , (*p3)++);
printf(" *p1 = %d, *p2 = %d, *p3 = %d\n",
*p1 , *p2 , *p3);
return 0;
}
来看运行结果; 分析: 一元运算符 * 和 ++ 的优先级相同,但结合律从左到右, *p1++: p1++ : 它先进性表达式运算,返回的指针是计算之前的指针:
i++ 的类似实现是 : int temp ; temp=i; i=i+1; //进行表达运算 return temp; // temp记录的是之前 i的值,返回之前的值
在开始进行*p1;所以得到的是序列的第一个值,100
*++p2: 先递增指针,
++ i的类似实现 i=i+1; return i // i的值已经得到递增了
再 进行 *p2// 这时指针已经指向下一个元素了 所以得到的是序列的第二个元素! 200
不过要说明的是: ++i 和 i++ 都可以使 i 进行递增,不过要在 赋值方面注意两者的区别即可
(*p3)++: 这个应该比较好分析, 指针解引用后值进行了 +1 , 得到 301
说明: ++ p1 和p2++ 都没有改变数组的值,而 (*p3)++确改变了数组中的值。
好了,这次的博客就到这里了。
|