IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 指针与地址 -> 正文阅读

[系统运维]指针与地址

1. 指针与地址

1.1 指针变量也是变量

指针变量也是变量,int **p: p 是个指针变量,p 中保存的是一个 int * 类型变量的地址,指向只指针的指针。

1.2 指针变量占用的空间

64位操作系统中,指针变量占 8 个字节;32位操作系统中,指针变量占 4 个字节。

1.3 指针代码演示

#include <stdio.h>

int main() {
    int num = 0x616263;
    //不当做整型变量看待,而是看做具有4个字节存储空间的数组
    printf("%s\n", (char *)(&num)); //先取num的地址,然后进行强转,输出结果为cba
    return 0;
}

num 并不会因为数据表现形式的不同而改变,num 是一个整型变量,占 4 个字节,也可以看做是长度为 4 的字符数组。
image-20201011172214601
每个十六进制数值需要 4 个二进制位表示,所以十六进制中两个数(占8位)为一个字节,因此是每两个数放在一个字节中。

为什么输出结果为cba?
十六进制的63,62,61对应的十进制值是99,98,97,ASCII码中对应于字符c,b,a,而0对应的就是字符\0,所以打印的时候遇到 \0 就结束了,因此结果为 cba

1.4 栈区地址的增长

#include <stdio.h>

int main() {
    int num1 = 0x616263;
    int num2 = 0x61626364;

    printf("%s\n", (char *)(&num1)); //先取num的地址,然后进行强转,输出结果为cba
    printf("%s\n", (char *)(&num2)); //输出结果为dcbacba
    printf("%s\n", (char *)(&num2 + 1));//num2是int类型,所以+1向后跳4个字节,因此结果为cba
    return 0;
}

为什么 num2 的输出结果为 dcbacba ?
首先,内存占用图如下:
image-20201011173313886
局部变量保存在栈区,num1num2 都要入栈,而栈区的增长是从高地址到低地址,也就呈现了上图中的内存占用分布。
输出 num2 的时候,从 num2 的首地址开始,一直到 \0 结束,所以就输出了 dcbacba

在阿里云上执行如下程序:

#include <stdio.h>

int main() {
	int num1 = 0x616263;
	int num2 = 0x61626364;
	printf("num1 = %s\n", (char *)(&num1)); //输出结果为cba
	printf("num2 = %s\n", (char *)(&num2)); //输出结果为dcba
}

先输出num1,结果为 cba;再输出num2,结果为 dcba。而如果没有第 6 行,num2 的输出结果为 dcbacba,出现这个结果的原因是阿里云使用的是 瓦片式存储,因为cba 是一样的,于是就叠加,即存储在一个位置。该存储方式的有点是提高内存空间利用率。

2. 函数指针与main函数

//函数指针变量:
int*add)(int, int);//add就是函数指针类型的变量,*是跟着当前变量的,加括号是为了与int* add(int,int)这种函数声明的情况区分

//类型。 typedef将变量提升至类型
typedef int (*add)(int, int); //typedef:类型重命名。add变成了函数指针类型

2.1 typedef 的用法

  • 内置类型的重命名:
typedef long long lint;
typedef char *pchar; //将char *重命名为pchar
  • 结构体类型的重命名
typedef struct __node {
	int x, y;
} Node, *PNode
//将struct __node重命名为Node
//将struct __node *重命名为PNode
  • 函数指针类型命名
typedef int (*func)(int);

2.1.1 #define 和 typedef

#include <stdio.h>
#define ppchar char *
typedef char * pchar;

int main() {
    pchar p1, p2;
    ppchar p3, p4;
    printf("p1 = %lu, p2 = %lu\n", sizeof(p1), sizeof(p2)); //8, 8
    printf("p3 = %lu, p4 = %lu\n", sizeof(p3), sizeof(p4)); //8, 1
    return 0;
}

sizeof(p4) 的值之所以为 1,是因为宏替换出现了问题。使用 g++ -E 查看宏替换后的代码:

typedef char * pchar;

int main() {
    pchar p1, p2;
    char * p3, p4; //p3是个指向char类型的指针变量(8个字节),而p4就是一个char类型的变量(1个字节)
    printf("p1 = %lu, p2 = %lu\n", sizeof(p1), sizeof(p2));
    printf("p3 = %lu, p4 = %lu\n", sizeof(p3), sizeof(p4));
    return 0;
}

宏只是进行了简单的替换,容易出错。

2.2 函数指针

  • 函数指针变量:int (*add)(int, int);
  • 函数指针类型:typedef int (*add)(int, int);

2.3 main函数参数

int main();
//argc表示命令行参数的个数;argv保存的是命令行的参数,argv是个一维数组,每个元素都是字符串,个数为argc个
int main(int argc, char *argv[]);
int main(int argc, char *argv[], char **env);//env可以看做一个二维字符数组,保存的是环境变量

操作系统调用 main 函数,返回值给操作系统。

2.3.1 main函数带2个参数

#include <stdio.h>

void output(int argc, char *argv[]) {
    printf("argc = %d\n", argc);
    for (int i = 0; i < argc; i++) {
        printf("%s\n", argv[i]);
    }
    return ;
}

//argc为传入主函数的参数个数, argv为2维的字符数组,具体传入的参数
int main(int argc, char *argv[]) {
    output(argc, argv);
    return 0;
}

运行结果:

./a.out
argc = 1
./a.out
./a.out  hello world      #参数以空格分隔                                                   
argc = 3
./a.out
hello
world
./a.out  "hello world"  #将hello world看做一个参数,用“”括起来
argc = 2
./a.out
hello world

2.3.2 main函数带 3 个参数

#include <stdio.h>

void output(int argc, char *argv[], char **env) {
    printf("argc = %d\n", argc);
    for (int i = 0; i < argc; i++) {
        printf("%s\n", argv[i]);
    }

    for(int i = 0; env[i]; i++) { //env[i]不为空的时候就可以一直输出
        printf("env[%d] = %s\n", i, env[i]);
    }
    return ;
}

//argc为传入主函数的参数个数, argv为2维的字符数组,具体传入的参数
//char **env:可以做是char *env[],当前env可以当做一维数组,里面存储的是一个一个的char *,即是指向char类型的地址,当地址为null的时候,就找到了最后一个环境变量的地址
int main(int argc, char *argv[], char **env) {
    output(argc, argv, env);
    return 0;
}

运行结果:

argc = 1
./a.out
#如下是获取到的环境变量
env[0] = TERM_SESSION_ID=w0t0p1:2F0E81F5-BB38-46DB-B596-5AE909019E39
env[1] = SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.u2uRbeIcuB/Listeners
env[2] = LC_TERMINAL_VERSION=3.3.12
env[3] = COLORFGBG=7;0
env[4] = ITERM_PROFILE=Default
env[5] = XPC_FLAGS=0x0
env[6] = LANG=zh_CN.UTF-8
env[7] = PWD=/Users/maureen/workspace/c_project/course
env[8] = SHELL=/bin/zsh
env[9] = TERM_PROGRAM_VERSION=3.3.12
env[10] = TERM_PROGRAM=iTerm.app
env[11] = PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Apple/usr/bin
env[12] = LC_TERMINAL=iTerm2
env[13] = COLORTERM=truecolor
env[14] = TERM=xterm-256color
env[15] = HOME=/Users/maureen
env[16] = TMPDIR=/var/folders/jl/14pjh5x96wd7xwk63xkwxcx80000gn/T/
env[17] = USER=maureen
env[18] = XPC_SERVICE_NAME=0
env[19] = LOGNAME=maureen
env[20] = ITERM_SESSION_ID=w0t0p1:2F0E81F5-BB38-46DB-B596-5AE909019E39
env[21] = __CF_USER_TEXT_ENCODING=0x0:25:52
env[22] = SHLVL=1
env[23] = OLDPWD=/Users/maureen/workspace/c_project
env[24] = ZSH=/Users/maureen/.oh-my-zsh
env[25] = PAGER=less
env[26] = LESS=-R
env[27] = LSCOLORS=Gxfxcxdxbxegedabagacad
env[28] = _=/Users/maureen/workspace/c_project/course/./a.out

可以使用 env[17] 即系统变量 USER 设置密码锁。

2.3.3 使用USER环境变量设置密码锁

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void output(int argc, char *argv[], char **env) {
    printf("argc = %d\n", argc);
    for (int i = 0; i < argc; i++) {
        printf("%s\n", argv[i]);
    }

    for(int i = 0; env[i]; i++) { //env[i]不为空的时候就可以一直输出,env[i]为字符串的首地址
        //printf("env[%d] = %s\n", i, env[i]);
        if (!strncmp(env[i], "USER=", 5)) { //比较前5个字符
            if (!strcmp(env[i] + 5, "maureen")) { //第i个元素中从第5个字符开始的字符串和“maureen"进行比较
                printf("welcome Maureen\n");
            } else {
                printf("You are not the user! Please gun!\n");
                exit(0);
            }
        }
    }
    return ;
}

//argc为传入主函数的参数个数, argv为2维的字符数组,具体传入的参数
//char **env:可以做是char *env[],当前env可以当做一维数组,里面存储的是一个一个的char *,即是指向char类型的地址,当地址为null的时候,就找到了最后一个环境变量的地址
int main(int argc, char *argv[], char **env) {
    output(argc, argv, env);
    return 0;
}
  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2021-08-28 09:45:32  更:2021-08-28 09:46:42 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 11:43:39-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码