指针是什么
指针是一类存储一个地址的变量。声明方式为<数据类型>* <变量名>.
所有的指针变量大小都是8个字节(64位系统)或者4个字节(32位系统),因为其中存储的是一个地址(16进制整数),这与指向的类型是无关的。但是,指向的变量类型必须与指针前面的<数据类型>一致。例如:
int A = 1;
int *pA = &A;
就是一个正确且典型的声明指向整型变量A的实例,但下面这个示例就是错的:
int A = 1;
char *pA = &A;
这是为什么呢?想一想,int类型的变量占4个字节,而char类型的变量占1个字节. 请注意一个内存单元是1字节的,这表明对于int A的引用需要引用4个字节,而对char类型的引用只需要1个字节. (应该是这样的吧)
使用*<变量名>可以对指针所指向的变量进行引用.
int* const <变量名>是一个指针常量,也就是指针本身是一个常量,指针所指向的地址不能修改,但指针所指向的内容可以修改.
const int* <变量名>是一个常量指针,也就是指针所指向的是一个常量,指针所指向的地址可以修改,但指针所指向的内容不可修改.
const int* const <变量名>是一个指向常量的常量指针,指针所指向的地址和内容都不可以修改.
int* const A | const int* A | const int* const A |
A是常量 | A是变量 | A是常量 |
指针所指地址是变量 | 指针所指地址是常量 | 指针所指地址是常量 |
那么,指针到底是什么呢?
例如,我们之前已经声明了一个整型变量A,A=1且存储在内存0x100。我们声明int* pA = &A后,pA是一个指针变量,其中存储的数据是0x100,也就是A的地址.
我们可以通过*pA对A进行间接引用 .
下面举个例子,假设现在要写一个swap函数,交换输入的两个参数,应该怎么办呢?
下面是一个似乎没有错误的写法:
void swap(int x, int y){
int t = x;
x = y;
y = t;
}
尝试后是不能交换x和y的(为什么呢?),理由如下:
step 1. 将输入的参数(定为a和b)赋值给函数内的形参x和y;
step 2. 将函数内的形参x, y交换 ;
step 3. 由于没有返回值,函数在这里运行完毕,x和y被释放掉.
那么应该怎么写呢?
void (int *x, int *y){
int t;
t = *x;
*x = *y;
*y = t;
}
这种写法是可行的,下面说明理由:
step 1. 传入参数&a和&b,将其赋值给x和y两个指针变量;
step 2. 通过指针变量x和y简介改变&a和&b中存储的值
step 3. 由于没有返回值,函数运行结束,x和y被释放掉.
我们注意到,由于这个写法是直接改变的a和b地址里对应的值,所以这种写法是切实可行的.
指针函数是和函数指针是什么
指针函数是一个返回值为指针的函数,声明方式为<数据类型>* 变量名(形式参数).
需要注意的是,指针函数不能返回一个过程中的地址,因为这些地址所指向的内存会被释放掉.
函数指针是一个指针变量,指向一个函数. 声明方式为<数据类型> (*函数名)(参数).
函数指针需要赋一个函数的地址值给它,赋值方式为:
int (*fun)(int x, int y);
fun = &Function;
或者:
int (*fun)(int x, int y);
fun = Function;
取地址符不是必须的,因为函数名本身就代表一个地址.
函数指针可以被这样调用:
u = f();
//或者
v = (*f)();
这两种方法是等价的. 第一种方法看起来和普通的函数调用没有什么区别.
实例:
int add(int x,int y){
return x+y;
}
int sub(int x,int y){
return x-y;
}
//函数指针
int (*fun)(int x,int y);
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//第一种写法
fun = add;
qDebug() << "(*fun)(1,2) = " << (*fun)(1,2) ;
//第二种写法
fun = ⊂
qDebug() << "(*fun)(5,3) = " << (*fun)(5,3) << fun(5,3);
return a.exec();
}
指针与字符串
已定义 | char s_array[]=”This is a book” | 是否可以 | Char* s_pt = “This is a book”; | 是否可以 |
直接输出 | cout << s_array; | 可以 | cout << s_pt; | 可以 |
直接输入 | cin >> s_array; | 可以 | cin >> s_pt; | 不可以 |
直接更改 | s_array = “OK”; | 不可以 | s_pt = “OK”; | 可以 |
赋值 | s_array = s_pt; | 不可以 | s_pt = s_array; | 可以 |
通过cin来修改指针所指的内容是不允许的,但是指针本身是可以被修改的。
string.h中提供了这些函数:
功能 | 函数形式 | 返回值 | 说明 |
字符串长度 | int strlen(const char *string); | 字符串长度. | ‘\0’不计入. |
字符串复制 | char *strcpy(char *dest, const char *src); | 把 src 所指向的字符串复制到 dest,替换对应的字符(scr中的’\0’也会参与替换). 该函数返回一个指向最终的目标字符串 dest 的指针. | 注意缓冲区溢出问题. |
按字符数复制 | char *strncpy(char *dest, const char *src, size_t n); | 把 src 所指向的字符串复制到 dest,最多复制 n 个字符. 该函数返回一个指向最终的目标字符串 dest 的指针. | 当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充. |
字符串按字符数比较 | int strncmp(const char *str1, const char *str2, size_t n); | strncmp() 函数通常用于比较两个字符串,以确定它们是否相等或哪个字符串在字典顺序上更小. 返回一个整数. | 如果返回值 < 0,则表示 str1 小于 str2。 如果返回值 > 0,则表示 str1 大于 str2。 如果返回值 = 0,则表示 str1 等于 str2。 |
字符串比较 | int strcmp(const char *str1, const char *str2); | 把 str1 所指向的字符串和 str2 所指向的字符串进行比较. | 如果返回值 < 0,则表示 str1 小于 str2。 如果返回值 > 0,则表示 str1 大于 str2。 如果返回值 = 0,则表示 str1 等于 str2。 |
字符串连接 | char *strcat(char *dest, const char *src); | 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾. 该函数返回一个指向最终的目标字符串 dest 的指针. | dest数组需要有足够的空间用以容纳src. src中的内容不会覆盖dest中的内容. |
对于const char*,可以使用”string”作为参数;对于char *,只能用一个数组名(地址)作为参数.
指针和数组
数组名本身就是地址,对于一维数组,它指向第一个(索引为0)元素.
指针和数组名联系起来,可以通过指针访问数组.
对于二维数组(假设为p[3][4]),它指向第一行第一列.
一维数组
一维数组名可以看作指针,例如int A[100];,数组名A和&A[0]都是访问第一个元素的地址.
通过*A就可以间接访问数组A的第一个(索引为0)元素,若要访问数组A中的第i-1个(索引为i)元素,可以采用:
*(A+i),这等价于A[i].
例如:
int A[10], *pa = A;//等价于: int [A]; int* pa; pa = A;
//注意后面的那句话中pa = A,pa前不加*是因为pa和A都是存储地址的.
//那么我们可以继续注意到:
*(pa+1);
*(A+1)
A[1];
*pa++; //等价于*(pa++),++是右结合运算符,比*优先.
//都是等价的
指针数组
指针数组是一个数组,其元素是指针.
声明:<数据类型>* <数组名>[常数];
例如:char *membet_name[] = {“Merry”, “John”, “Hill”};
数组元素指向一些不同长度的字符串。下面举个例子:
char *membet_name[] = {"Merry", "John", "Hill"};
for(int i = 0; i <= 2; i++){
cout << member_name[i] << endl;
}
输出为:
Merry
John
Hill
指针数组可以作为形式参数(例如传入main函数,下面是一个示例)
int main(int argc, char* argv[]){
cout << "共输入了" << argc << "个参数," << "分别是\n";
for(int i = 0; i < argc; ++i){
cout << argv[i]<< ' ';
}
}
二维数组
二维数组名可以看作指针,它具有以下特点:
二维数组可以看成是一维数组的一维数组
对二维数组B[3][4],数组名B的地址,是其中第一个一维数组B[0]的地址(这恰好也是B[0][0]的地址,所以B指向的元素恰好就是B[0][0]!)。
访问第i行j列的数组元素(i,j指的是索引):
*(*(<数组名>+i)+j)
B[0][0]默认访问的元素,等于*B,也是二维数组中的第一个一维数组 | *(*B+1) | *(*B+2) |
*(B+1)所访问的地址,也是二维数组中的第二个一维数组 | *(*(B+1)+1) | *(*(B+1)+2) |
*(B+2)所访问的地址,也是二维数组中第三个一维数组. | *(*(B+2)+1) | *(*(B+2)+2) |
假设数组名为B.
指针和结构体
下面声明一个结构体:
struct student{
int id;
float score;
};
stu = {123, 123};
//指针访问结构体中的元素有点操作符"."和尖头操作符"->".
student* ps = &stu;
cout << (*ps).score << ps->score;//均是输出stu的score.
也可以使用new来申请分配一块空间存放
struct Employee{
char name[20];
unsigned long id;
float salary;
};
int main(){
Employee* prPtr = new Employee; //直接指定一块新的空间用来存放
strcpy(prPtr->name, "Zhang San"); // 将new Employee中存放的Employee的name变为张三
prPtr->id = 1145141919810;
prPtr->salary = 114514.1919810;
cout << prPtr->name << prPtr->id << prPtr->salary << endl;
delete prPtr; //删除申请的prPtr空间
链表
例:单向链表的建立:
struct student{
long num;
char name[20];
float score;
student* next; //指向下一个学生的指针
};
int main(){
student* head = NULL, *temp = NULL;
head = new student;//head为头结点指针
temp = head;
int i = 1;
while(temp != NULL){
temp->num = i;
cout << "PLZ input name and score for No." << i << endl;
cin >> temp->name >> temp->score;
temp->next = NULL;
i++;
}
void类型指针
void指针也指向内存地址,但是不指定这个地址单元的数据类型.
声明:void* <指针名>;
void*可以接受任意类型的指针赋值,但是void*不能直接复制给其它指针,如果要将 void 指针 p 赋给其他类型的指针,则需要强制类型转换。按照ANSI标准,不能对 void 指针进行算法操作。
路过康康