指针(1)

指针是什么

指针是一类存储一个地址的变量。声明方式为<数据类型>* <变量名>.

所有的指针变量大小都是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 Aconst int* Aconst 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 指针进行算法操作。

评论

  1. 懒得起名字
    2 年前
    2024-2-09 23:16:52

    路过康康

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇