总结了一些指针易出错的常见问题(一)

1.认识指针

  1.1 指针和内存

    C程序在编译后,会以三种形式使用内存:

    1)静态/全局内存

      静态/全局声明的变量在这里。这些变量从程序开始运行时分配,直到程序终止才消失。

      所有函数都能访问全局变量,静态变量的作用域则局限在定义它们的函数内部。

    2)自动内存

      这些变量在函数内部声明,并且在函数调用时才创建,他们的作用域局限于函数内部,

      而且声明周期限制在函数的执行时间内。

    3)动态内存

      内存分配在堆上,可以根据需要释放,而且直到释放才消失。指针引用分配的内存,作用域

      局限于引用内存的指针。

    数组和指针实现员工链表时情况:

1.创建数组时需要知道数组的长度,这样就会限制链表所容纳的元素数量。

2.指针没有上面的限制,新节点可以根据需要动态分配。malloc和free函数分配和释放动态内存。

Tips:

1.如何阅读指针声明,那就是倒过来读

const int *pci     // pci 是一个指向整数常量的指针变量

2.在不同平台上用一致的方式显示指针的值比较困难。一种方法就是把指针(强制)转化为void指针,然后用%p/%o/%x格式说明符来显示。

printf("Value of pi: %p\n", (void*)pi);

void指针是通用指针,用来存放任何数据类型的引用。任何指针都可以被赋给void指针,它可以被转换为原来的指针类型。

void只用作数据指针,不能用作函数指针。

3.指针被声明为全局/静态。就会在程序启动时被初始化为NULL。

  1.2 指针操作符

    

    直接间接引用

      指针可以用不同的间接引用层级。双重指针**。

char *titles[]={"ajj","bbs","klc"};
        char **bestbooks[2];
        char **english[1];
          bestbooks[0]=&titles[0];
          bestbooks[1]=&titles[1];
          english[2]=&titles[2];

    常量和指针

2. C的动内存管理 malloc/realloc/calloc/free

  malloc分配的是字节数/  sizeof(字节数)

内存泄漏:丢失内存地址,应该调用free函数没有调用

    迷途指针

3.指针和函数     指针函数/函数指针/程序栈/堆

  程序栈(程序栈通常占据区域下部,堆则上部,栈向上生长,堆向下生长)

用指针传递数据的一个主要原因就是函数可以修改数据。

  

4. 函数指针和指针函数

为函数指针声明一个类型定义会比较方便。类型定义的如下:

typedef int (*funcptr)(int);

funcptr fptr2;

fptr2=square;

......

#include<iostream>
using namespace std;
int main()
{
    int square(int num);
    int (*fptr1)(int); //类似于定义变量
    typedef int (*funcptr)(int);
    funcptr fptr2;
    fptr2=square;
    int n=5;
    fptr1=&square;//此时也可以将其替换为   fptr1=square, 函数名表示函数入口地址
    cout<<"5 square is "<<fptr1(n)<<endl;
    cout<<"5 square is "<<fptr2(n)<<endl;
    return 0;
}
 int square(int num)
 {
     return num*num;
 } 

传递函数指针,只要把函数指针声明作为函数参数(形参)即可。

#include<iostream>
using namespace std;

 int add (int num1,int num2)
    {
    return num1+num2;
    }

int sub(int num1,int num2)
    {
        return num1-num2;
    }

typedef int (*fptr)(int,int);
    int compute(fptr operation, int num1,int num2)
    {
        return operation(num1,num2);
    }

    int main()
{
    cout<<compute(add,5,8)<<endl;
    cout<<compute(sub,5,8)<<endl;
    return 0;
 } 

返回函数指针需要把函数的返回类型声明为函数指针。(注意注释部分////)

#include<iostream>
using namespace std;

 int add (int num1,int num2)
    {
    return num1+num2;
    }
    int sub(int num1,int num2)
    {
        return num1-num2;
    }
    typedef int (*fptr)(int,int);
    int compute(fptr operation, int num1,int num2)
    {
        return operation(num1,num2);
    }
//////////////////////////////////////////////
    fptr select(char opcode)
    {
        switch(opcode)
        {
            case ‘+‘:return add;
            case ‘-‘:return sub;
        }
    }

    int evalute(char opcode,int num1,int num2)
    {
        fptr operation=select(opcode);
        return operation(num1,num2);
    }
///////////////////////////////////////////////
    int main()
{
    cout<<evalute(‘+‘,5,8)<<endl;
    cout<<evalute(‘-‘,5,8)<<endl;
    return 0;
 } 

使用函数指针数组可以基于某些条件选择要执行的函数,只要把这些函数指针声明为数组类型即可。定义如下:

#include<iostream>
using namespace std;

 int add (int num1,int num2)
    {
    return num1+num2;
    }
    int sub(int num1,int num2)
    {
        return num1-num2;
    }
    typedef int (*fptr)(int,int);
    int compute(fptr operation, int num1,int num2)
    {
        return operation(num1,num2);
    }
//////////////////////返回函数指针////////////////////////
    fptr select(char opcode)
    {
        switch(opcode)
        {
            case ‘+‘:return add;
            case ‘-‘:return sub;
        }
    }

    int evalute(char opcode,int num1,int num2)
    {
        fptr operation=select(opcode);
        return operation(num1,num2);
    }
/////////////////////使用函数指针数组//////////////////////////
typedef int (*operation)(int,int);
operation operations[128]={NULL};
void initializeopArray()
{
    operations[‘+‘]=add;
    operations[‘-‘]=sub;
}
int evaArray(char opcode,int num1,int num2)
{
    fptr operation;
    operation=operations[opcode];
    return operation(num1,num2);
}
/////////////////////////////////////////////////////
    int main()
{
    initializeopArray();
    cout<<evalute(‘+‘,5,8)<<endl;
    cout<<evalute(‘-‘,5,8)<<endl;
    cout<<evaArray(‘+‘,5,8)<<endl;
    cout<<evaArray(‘-‘,5,8)<<endl;
    return 0;
 } 

小结:理解程序栈和堆有助于更深入彻底理解程序的工作方式以及指针的行为。

时间: 2024-07-31 16:41:55

总结了一些指针易出错的常见问题(一)的相关文章

总结了一些指针易出错的常见问题(六)

安全问题与指针误用 声明和初始化指针(初始化失败) 误用指针 释放问题 指针声明: // 宏定义(仅仅是替换) #define PINT int*; PINT ptr1, ptr2;其实是定义 int* ptr1, ptr2;一个指针,一个整型常量. //typedef 命名已有数据类型(优于宏定义) typedef int* PINT; PINT ptr1, ptr2; 宏定义和typedef区别参考:浅谈c/c++typedef和#define区别 //  宏定义  和  typedef区别

总结了一些指针易出错的常见问题(三)

指针与字符串 NULL和NUL区别:前者用来表示特殊的指针((void*)0),而NUL是一个char(\0),不可混用. 字符常量:单引号:字符串:双引号: 字符串的声明方式:字面量.字符数组.指针. 字符串字面量池: 字符串初始化 初始化char数组:   char header[]="Media Player"; strcpy函数初始化数组 char header[13]; strcpy(header, "Meadia Player"); 2.初始化char指

总结了一些指针易出错的常见问题(四)

指针与结构体 简介:我们可以使用C的结构体来表示数据结构元素,比如链表或树的节点,指针是把这些元素联系到一起的纽带. typedef struct _person{ char* firstName; char* lastName; char* title; unsigned int age; } Person;/*用点表示法初始化*/ Person person;person.firstName=(char*)malloc(strlen("Emily")+1);stcpy(person

总结了一些指针易出错的常见问题(七)

指针的其他重要内容 主要研究以下问题 指针的类型转换 访问硬件设备 别名和强别名 使用restrict关键字 线程(一是如何用指针在线程之间共享数据这个基本问题:二是如何用指针支持回调函数(一个操作可能会调用某函数来执行任务,如果实际被调用的函数发生了改变,我们称之为回调函数.)) 面向对象技术 转换指针 类型转换是一种基本操作,跟指针结合使用时很有用. 访问有特殊目的的地址: 分配一个地址来表示端口: 判断机器的字节数: 整数转换为整数指针: int num =8; int *pi = (in

总结了一些指针易出错的常见问题(二)

4.指针与数组  一些常见的错误观点是数组和指针是完全可以互换的.尽管数组名字有时候可以当指针来使用,但是数组的名字不是指针. 数组是能用索引访问的同质元素连续集合.(连续是指数组的元素在内存中是相邻的,中间不存在空隙同质是指元素是同一类型) 指针在处理数组时很有用,既可以用指针指向已有的数组,也可以从堆上分配内存然后把这块内存当做一个数组使用. 数组/指针表示法 给数组地址加1实际加了4,也就是整数的长度. #include<iostream> using namespace std; in

mysql安装及易出错问题解决

.双击.msi 安装包 2.勾选同意,下一步 3.一直 下一步,遇到弹窗选择ok,然后点击Execute 4.check  ok后点击下一步 5.next 6.设置root密码,next 7.无脑next后,来到易出错界面了 - -! (启动服务失败--解决方案:1.卸载完全之前安装的mysql,可能有残留文件导致服务不能启动 2. MySQL安装是出现could not start the service mysql error:0 提示错误,解决方法:  首 先,在管理工具->服务里面将My

jQuery选择器与JavaScript易出错知识点

一.jQuery选择器 基本选择器 1.Id(根据给定的ID匹配一个元素.如果选择器中包含特殊字符,可以用两个斜杠转义.) jQuery代码为$("#myclass") 若有特殊字符则 HTML代码为<span id="myclass[1]" jQuery代码为$("#myclass\\[1\\]") 2.Class(一个用以搜索的类.一个元素可以有多个类,只要有一个符合就能被匹配到) jQuery代码为$(".myclass&q

如何给一个完全不懂的非程序员解释复杂的、耗时的、易出错的软件开发的过程?

简评:相当有趣的一篇文章,教导你如何给非程序员讲清楚最难.最复杂的那些开发问题. 回答这个问题首先你应该解释一下如何泡一壶茶: 烧水 把茶叶放在锅里 当水烧开后,把它倒入锅里 等待 5 分钟 把茶倒进杯子里 喝 现在开始变得有趣了.你需要开始问他们这些问题: 烧水? 水来自哪里? 水壶在哪里呢? 你怎么把水弄到水壶里去呢 你怎么知道放了多少水 如果没有水 / 水壶 / 电怎么办? 如果你的填充传感器失败了怎么办? 如果你的沸腾传感器失败了? 把茶叶放在锅里? 锅在哪里,如果没有一个呢?我们应该在

C提高_day03_一级指针易犯错误模型

1.char *(字符串)做函数参数出错模型分析 建立一个思想:是主调函数分配内存,还是被调用函数分配内存: //不要相信,主调函数给你传的内存空间,你可以写......一级指针你懂了. 但是二级指针,你就不一定懂...抛出......... void copy_str21(char *from, char *to) { if (*NULL = '\0' || *to!='\0') { Printf("func copy_str21() err\n"); return; } for (