C语言程序与设计——指针地址与main函数

指针变量

在C语言中,最重要的就是对于指针和地址的理解,因为C语言是更接近底层的编程语言,所以它可以允许开发者对内存操作,这也是区别于其它编程语言的一个重要特性。
如何对内存进行操作呢。我们知道在编程过程中,在任何语言中,我们在定定义一个变量的过程中,实际上就是向操作系统申请了一块内存,但由于其它编程语言不支持我们对于内存进行操作,所以我们只知道申请了一块内存,但不知道该位置在哪。由于C语言支持我们对于内存的操作,所以我们可以通过在变量前加一个取地址符&,得到该内存的地址,数组的话无需加取地址符。

#include<stdio.h>
int main(){
    int a[5];
    int b;
    printf("&b = %p, sizeof(&b) = %lu\n", &b, sizeof(&b));
    printf("a[0] = %p\n", a);
    printf("a[1] = %p\n", a + 1);
    printf("a[2] = %p\n", a + 2);
    printf("a[3] = %p\n", a + 3);
    printf("a[4] = %p\n", a + 4);
}

运行结果
>>&b = 0x7fffe87408ec, sizeof(&b) = 8
a[0] = 0x7fffe87408f0
a[1] = 0x7fffe87408f4
a[2] = 0x7fffe87408f8
a[3] = 0x7fffe87408fc
a[4] = 0x7fffe8740900

可以看到整型数组的地址间隔刚好为4,也就是说明整型变量大小是四个字节。而且指针的大小是8个字节。

指针的大小
在我所演示的运行环境是在64位操作系统下运行的,64bit刚好是8字节,也就是说如果在32位操作系统下运行该程序的话sizeof(&b)会是4字节

  • 指针的大小是与系统环境相关的
  • 以int为例,int占用四个字节,在取地址时从相关地址的首地址开始
  • 在一个32bit操作系统中,地址最多为4GB,所以假设插入一个8GB的内存条则会有另外4GB无法使用

变量的地址

指针变量也是变量,也就说,指针变量同样也有地址。由此,我们就可以对数组的各个地址取地址符,这样就构成了二维数组。

#include<stdio.h>

int main(){

    int a;
    int *p = &a;
    int arr[3][3];
    printf(" &a = %p\n p = %p\n &p = %p\n", &a, p, &p);
    printf(" arr[0][0] = %p, sizeof(arr[0][0]) = %lu\n", arr, sizeof(&arr));
    printf(" arr[1][0] = %p, sizeof(arr[1][0]) = %lu\n", arr + 3, sizeof(arr + 3));
    printf(" arr[1][0] = %p, sizeof(arr[2][0]) = %lu\n", &arr + 1, sizeof(arr + 3 + 3 + 1));

}

  
 运行结果
 >> &a = 0x7fff350f5c54
 p = 0x7fff350f5c54
 &p = 0x7fff350f5c58
 arr[0][0] = 0x7fff350f5c60, sizeof(arr[0][0]) = 8
 arr[1][0] = 0x7fff350f5c84, sizeof(arr[1][0]) = 8
 arr[1][0] = 0x7fff350f5c84, sizeof(arr[2][0]) = 8

理解了双重指针以及二维数组之后,那么就可以尝试高维数组的实现形式。

所以在scanf(“%d”, &n);这行代码中,我们传入的是地址,因为我们要改变的是n所代表的这块内存上的值,所以需要找到内存的所在地址然后存入数据。

函数指针

在之前函数的章节,已经涉及到了函数指针,但只是把函数作为传入参数的形式,下面我们将演示函数指针的声明形式

#include<stdio.h>

int add(int a, int b){
    return a + b;
}
int main(){
    int (*func)(int, int);
    func = add;
    printf("%d", func(1,1));
}
#include<stdio.h>

int add(int a, int b){
    return a + b;
}
int main(){
    typedef int (*func)(int, int);
    func f = add;
    printf("%d", f(1, 1));
}

可以看到我们使用了两种方式定义了函数指针。可以看到以第一种是直接声明的了一个函数指针,而后将指针指向了add()函数,那么第二种的话用到了一个关键字typedef,而后func变成了一个类型的样子,随后定义了f指针指向了add()函数。二者的定义形式的区别就是

声明一个函数指针变量: int (*add)(int, int);
定义一个函数指针类型: typedef int (*add)(int, int);

Main函数

我们知道在C语言中,一个程序运行必须要有main函数,但是我们在使用main函数中,基本都是无参数的形式,但实际上的main函数有三种形式,如下。下面就简单了解一下main函数的其他两种形式

int main();
int main(int argc, char *argv[]);
int main(int argc, char *argv[], char **env);

参数解释:
argc: 命令行参数个数(例如 ls -al al就是参数)
*argv[ ]:命令行内容(注意这是一个二位数组)
**env:环境变量

#include<stdio.h>

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

int main(int argc, char *argv[], char **env){
    output (argc, argv,env);
    return 0;
}

在这里插入图片描述
可以看到示例 ./a.out -a -l -s。其中作为参数全部传入了程序中,值得一提的是./a.out也作为了一个参数传入了,也就是说程序在运行时至少会有一个命令行参数。我们也不难发现空格是参数的分隔符,但是我们可以通过加\转义字符,使其可以把空格传入。

#include<stdio.h>

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


int main(int argc, char *argv[], char **env){
    output (argc, argv,env);
    return 0;
}

在这里插入图片描述
可以看到env参数包含了很多我们的环境变量,包括用户路径语言等等。这些都是操作系统为我们提供的接口允许我们可以做在操作系统中做一些底层的操作。

main()返回值

在我们初学C语言时就接触了main()函数,我们可以不用返回值,但是又会经常见到return 0。那么main的返回值是返回给谁,又有什么作用呢?从函数上来讲,当然是谁调用就返回给谁,该函数是操作系统调用的,所以就是返回给操作系统的。
我们可以通过echo $?的命令查看上一次运行是不是成功运行,如果返回值为0,代表执行失败。如果是1则是成功执行,所以说可能对于我们来讲程序的功能是成功执行的,但是对于操作系统来讲我们的程序没有执行成功。所以我们应尽量在主函数中加入返回值。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/515675.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

续二叉搜索树递归玩法

文章目录 一、插入递归二、寻找递归&#xff08;非常简单&#xff0c;走流程就行&#xff09;三、插入递归&#xff08;理解起来比较麻烦&#xff09; 先赞后看&#xff0c;养成习惯&#xff01;&#xff01;&#xff01;^ _ ^<3 ❤️ ❤️ ❤️ 码字不易&#xff0c;大家的…

ruoyi-nbcio-plus基于vue3的flowable流程设计器主界面升级修改

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://122.227.135.243:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a…

瑞_Redis_商户查询缓存

文章目录 项目介绍1 短信登录2 商户查询缓存2.1 什么是缓存2.1.1 缓存的应用场景2.1.2 为什么要使用缓存2.1.3 Web应用中缓存的作用2.1.4 Web应用中缓存的成本 2.2 添加Redis缓存2.2.1 背景2.2.2 缓存模型和思路2.2.3 代码实现2.2.4 测试附&#xff1a;IDEA控制台输出自动换行设…

Railway免费部署Flowise AI工作流教程

&#x1f9d9;‍♂️ 诸位好&#xff0c;吾乃斜杠君&#xff0c;编程界之翘楚&#xff0c;代码之大师。算法如流水&#xff0c;逻辑如棋局。 &#x1f4dc; 吾之笔记&#xff0c;内含诸般技术之秘诀。吾欲以此笔记&#xff0c;传授编程之道&#xff0c;助汝解技术难题。 &#…

C++中的vector与C语言中的数组的区别

C中的vector和C语言中的数组在很多方面都有所不同&#xff0c;以下是它们之间的一些主要区别&#xff1a; 大小可变性&#xff1a; vector是C标准模板库&#xff08;STL&#xff09;提供的动态数组容器&#xff0c;它的大小可以动态增长或减少。这意味着你可以在运行时添加或删…

常见滤波算法(PythonC版本)

简介 受限于MCU自身的ADC外设缺陷&#xff0c;精度和稳定性通常较差&#xff0c;很多场景下需要用滤波算法进行补偿。滤波的主要目的是减少噪声与干扰对数据的影响&#xff0c;让数据更加接近真实值。 一阶低通滤波 一阶低通滤波是一种信号处理技术&#xff0c;用于去除信号中…

Verilog奇技淫巧(二)

1. Verilog系统函数及其作用总结 $time用来查看当前仿真时刻&#xff0c;返回一个64bit的整数来表示的当前仿真时刻&#xff1b; $ realtime和$time的作用相同&#xff0c;$realtime但是返回的时间数字是一个实型数&#xff1b; $readmemb&#xff0c;用来从文件中读取数据到…

从redux的基本概念渐进式理解redux/toolkit的用法

概念 Redux toolkit是帮助提高redux开发效率的一个库 React-redux 是将React和Redux toolkit绑定在一起的一个库 action 是一个对象,里面有一个type属性 action creator是一个函数,这个函数可以返回上面的action对象。 reducer 是一个函数,接受两个参数(initilastate, acti…

北京哪位医生治疗糖尿病比较好?

糖尿病是一种常见的慢性疾病&#xff0c;主要是由于胰岛素分泌不足或利用障碍导致的以高血糖为特征的代谢性疾病。持续的高血糖和长期的代谢紊乱可能导致全身组织器官&#xff0c;特别是眼、肾、心血管及神经系统的损害和功能障碍。 北京崇文门医院朱学敏主任在糖尿病领域有有着…

LLM端侧部署系列 | 如何将阿里千问大模型Qwen部署到手机上?实战演示(下篇)

引言 简介 编译Android可用的模型 转换权重 生成配置文件 模型编译 编译apk 修改配置文件 绑定android library 配置gradle 编译apk 手机上运行 安装 APK 植入模型 效果实测 0. 引言 清明时节雨纷纷&#xff0c;路上行人欲断魂。 小伙伴们好&#xff0c;我是《小…

Matlab有限元编程案例全家桶【源码+理论文本】

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…

制作一个RISC-V的操作系统九-上下文切换和协作式多任务(任务 任务上下文 多任务 多任务系统分类 协作式多任务 代码实现 )

文章目录 任务任务上下文多任务多任务系统分类协作式多任务代码实现asm volatile("csrw mscratch, %0" : : "r" (x));lw和swp tp 和 zero寄存器 待实现 任务 理解为一个段要执行的指令 任务上下文 理解为执行该任务时对应的CPU的寄存器各个状态 多任务…

Java并发编程基础面试题详细总结

1. 什么是线程和进程? 1.1 何为进程? 进程是程序的一次执行过程&#xff0c;是系统运行程序的基本单位&#xff0c;因此进程是动态的。系统运行一个程序即是一个进程从创建&#xff0c;运行到消亡的过程。 在 Java 中&#xff0c;当我们启动 main 函数时其实就是启动了一个…

【重学C语言】四、运算符和表达式

【重学C语言】四、运算符和表达式 概念左值与右值运算符一元运算符二元运算符三元运算符 优先级结合性 基本运算符赋值运算符算术运算符复合赋值运算符位运算符应用条件和逻辑运算符条件运算符逻辑运算符逻辑短路逻辑与&#xff08;&&&#xff09;的短路行为逻辑或&…

git版本控制工具

一、git工具介绍 1. 版本控制的概念与分类 版本控制是一种管理文件变更的技术&#xff0c;允许开发者记录文件或一组文件随时间的变化&#xff0c;以便能够恢复到先前的状态、查看历史更改、对比不同版本之间的差异&#xff0c;以及协同工作。版本控制系统主要有两种分…

jsp中使用session实现一个简单的购物车案例

1、需求分析 相信大家都有网上购物的经历&#xff0c;当用户选定某件商品时&#xff0c;只要选择“购买”&#xff0c;便可将商品添加到购物车中&#xff0c;购物车中包含了用户所有要购买的商品。接下来&#xff0c;以购买图书为例&#xff0c;使用Session模拟实现购物车功能…

vscode开发ESP32问题记录

vscode 开发ESP32问题记录 1. 解决vscode中的波浪线警告 1. 解决vscode中的波浪线警告 参考链接&#xff1a;https://blog.csdn.net/fucingman/article/details/134404485 首先可以通过vscode 中的IDF插件生成模板工程&#xff0c;这样会自动创建.vscode文件夹中的一些json配…

illustrator编组后透明图形也能挡住后面的图形的物体

今日学习使用illustrator&#xff0c;使用3D中的突出和斜角生成了一个圆柱体之后&#xff0c;扩展图形&#xff0c;修改之后发现一个奇怪的问题&#xff0c;编组中的透明物体挡住了后面的图形&#xff08;后面的图形是有颜色的&#xff09; 后面发现&#xff0c;原来是扩展生成…

Redis 事务 与 管道

redis事务 谈到事务大家可能就会想起mysql中的事务 注意这里的事务不是指的是事务的四大特性acid 持久性 原子性 隔离性 一致性 事务的概念就是 一组命令,串行化执行而不被打断 这里redis的事务和mysql的事务就不太一样 传统关系型数据库的事务主要强调的是一个没有执行完成就…

如何查询网站是否被搜索引擎收录

怎么看网站有没有被百度收录 对于网站所有者来说&#xff0c;了解自己的网站是否被百度搜索引擎收录是非常重要的。只有被收录&#xff0c;网站才能在百度搜索结果中展现&#xff0c;从而获取流量和曝光。下面介绍几种方法&#xff0c;让您快速了解自己的网站是否被百度收录。…