编程之基 --- C语言基础大全 III
BACK:编程之基 --- C语言基础大全 II
流程一览
- Eighth Week 数组
- 1. 数组
- 2. 数组运算
- 2.1 素数
- 2.2 二维数组
- Ninth Week 指针
- 1. 指针
- 2. 指针应用
- 3. 常见错误
- 4. 数组和指针
- 5. 指针和const
- 6. 指针运算
- 7. 动态分配内存
Eighth Week 数组
1. 数组
定义数组:
<类型> 变量名称[元素数量];
int grades[100];
double weight[20];
元素数量必须是整数
C99之前:元素数量必须是编译时刻确定的字面量
数组:
- 是一种容器(放东西的东西),特点是:
- 其中所有的元素具有相同的数据类型;
- 一旦创建,不能改变大小
- *(数组中的元素在内存中是连续依次排列的)
有效的下标范围:
- 编译器和运行环境都不会检查数组下标是否越界,无论是对数组单元做读还是写
- 一旦程序运行,越界的数组访问可能造成问题,导致程序崩溃
- segmentation fault
- 但是也可能运气好,没造成严重的后果
- 所以这是程序员的责任来保证程序只使用有效的下标值:[0,数组的大小-1]
int a[0]; // 可以存在,但是无用
统计个数示例:
const int number = 10; // 数组的大小,c99
int x;
int count[number]; // 定义数组
int i;for (i=0; i<number; i++) { // 初始化数组count[i] = 0;
}
scanf("%d", &x);
while (x!=-1) {if (x>=0 && x<=9) {count[x]++; // 数组参与运算}scanf("%d", &x);
}
for (i=0; i<number; i++) { // 遍历数组输出printf("%d:%d\n", i, count[i]);
}
2. 数组运算
集成初始化时的定位:
int a[10] = { // c99 ONLY[0] = 2, [2] = 3, 6,
};
- 用[n]在初始化数据中给出定位
- 没有定位的数据接在前面的位置后面
- 其他位置的值补零
- 也可以不给出数组大小,让编译器算
- 特别适合初始数据稀疏的数组
数组的大小:
sizeof给出整个数组所占据的内容的大小,单位是字节
sizeof(a)/sizeof(a[0])
sizeof(a[0])给出数组中单个元素的大小,于是相除就得到了数组的单元个数
这样的代码,一旦修改数组中初始的数据,不需要修改遍历的代码
数组的赋值:
int a[] = {2, 4, 6, 7, 1, 3};
int b[] = a;
- 数组变量本身不能被赋值
- 要把一个数组的所有元素交给另一个数组,必须采用遍历
for (i=0; i<length; i++) {b[i] = a[i];
}
- 数组作为函数参数时,往往必须再用另一个参数来传入数组的大小
- 数组作为函数的参数时:
- 不能在[]中给出数组的大小
- 不能再利用sizeof来计算数组的元素个数!
/*
找出key在数组a中的位置
@param key 要寻找的数字
@param a 要寻找的数组
@param length 数组a的长度
@return 如果找到,返回其在a中的位置;如果找不到则返回-1
*/
int search(int key, int a[], int length);int main(void) {int a[] = {2, 4, 6, 7, 1, 3, 5, 9};int x;int loc;printf("请输入一个数字:");scanf("%d", &x);loc = search(x, a, sizeof(a)/sizeof(a[0]));if (loc != -1) {printf("%d在第%d个位置上\n", x, loc);} else {printf("%d不存在\n", x);}return 0;
}int search(int key, int a[], int length) {int ret = -1;int i;for (i=0; i<length; i++) {if (a[i] == key) {ret = i;break;}}return ret;
}
2.1 素数
判断是否能被已知的且<x的素数整除:
int isPrime(int x, int knownPrimes[], int numberOfKnownPrimes) {int ret = 1;int i;for (i=0; i<numberOfKnownPrimes; i++) {if (x%knownPrimes[i] == 0) {ret = 0;break;}}return ret;
}int main(void) {const int number = 100;int prime[number] = {2};int count = 1;int i = 3;while (count < number) {if (isPrime(i, prime, count)) {prime[count++] = 1;}i++;}for (i=0; i<number; i++) {printf("%d", prime[i]);if ((i+1)%5) printf("\t");else printf("\n");}return 0;
}
构造素数表:
- 预构造n以内的素数表
- 令x为2
- 将2x、3x、4x直至ax<n的数标记为非素数
- 令x为下一个没有被标记为非素数的数,重复2;直到所有的数都已经尝试完毕
- 构造n以内(不含)的素数表
- 开辟prime[n],初始化其所有元素为1,prime[x]为1表示x是素数
- 令x=2
- 如果x是素数,则对于 (i=2; x*i<n; i++) 令 prime[i*x]=0
- 令x++,如果x<n,重复3,否则结束
#include <stdio.h>int main() {const int maxNumber = 25;int isPrime[maxNumber];int i;int x;for (i=0; i<maxNumberl i++) {isPrime[i] = 1;}for (x=2; x<maxNumber; i++) {if (isPrime[x]) {for (i=2; i*x<maxNumber; i++) {isPrime[i*x] = 0;}}}for (i=2; i<maxNumber; i++) {if (isPrime[i]) {printf("%d\t", i);}}printf("\n");return 0;
}
2.2 二维数组
二维数组的初始化:
int a[][5] = {{0, 1, 2, 3, 4},{2, 3, 4, 5, 6},
};
- 列数是必须给出的,行数可以由编译器来数
- 每行一个{},逗号分隔
- 最后的逗号可以存在,有古老的传统
- 如果省略,表示补零
- 也可以用定位(*C99 ONLY)
Ninth Week 指针
1. 指针
只能变量取地址
- 就是保存地址的变量
int i;
int* p = &i;
int* p, q;
int *p, q; // 与上一行所表达一样,p指针,q是int类型变量
访问那个地址上的变量*
- *是一个单目运算符,用来访问指针的值所表示的地址上的变量
- 可以做右值也可以做左值
int k = *p;
*p = k+1;
*左值之所以叫左值
- 是因为出现在赋值号左边的不是变量,而是值,是表达式计算的结果;
a[0] = 2;
*p = 3; // 赋值时,p代表的是地址,\*p代表的是地址所指的值。
- 是特殊的值,所以叫做左值
2. 指针应用
指针应用场景一:
- 交换两个变量的值
void swap(int *pa, int *pb) {int t = *pa;*pa = *pb;*pb = t;
}
指针应用场景二a:
- 函数返回多个值,某些值就只能通过指针返回
- 传入的参数实际上是需要保存带回的结果的变量
指针应用场景二b:
- 函数返回运算的状态,结果通过指针返回
- 常用的套路是让函数返回特殊的不属于有效范围内的值来表示出错:
- -1或0(在文件操作会看到大量的例子)
- 但是当任何数值都是有效的可能结果时,就得分开返回了
- 后续的语言(C++,Java)采用了异常机制来解决这个问题
3. 常见错误
定义了指针变量,还没有指向任何变量,就开始使用指针
4. 数组和指针
传入函数的数组成了什么?
- 函数参数表中的数组实际上是指针
sizeof(a) == sizeof(int*)
但是可以用数组的运算符[]进行运算
int isPrime(int x, int knownPrimes[], int numberOfKnownPrimes) {int ret = 1;int i;for (i=0; i<numberOfKnownPrimes; i++) {if (x%knownPrimes[i] == 0) {ret = 0;break;}}return ret;
}
数组变量是特殊的指针:
数组变量本身表达地址,所以
int a[10]; int *p=a; // 无需用&取地址
但是数组的单元表达的是变量,需要用&取地址
a == &a[0]
[]运算符可以对数组做,也可以对指针做:
- p[0] <==> a[0]
*运算符可以对指针做,也可以对数组做:
*a = 25;
数组变量是const的指针,所以不能被赋值
- int a[] <==> int *cosnt a=…
5. 指针和const
指针是const:
- 表示一旦得到了某个变量的地址,不能再指向其他变量(可以改所指地址变量的值,不可以改所指地址)
int* const q = &i; // q是const
*q = 26; // OK
q++; // ERROR
所指是const:
- 表示不能通过这个指针去修改那个变量(并不能使得那个变量成为const。变量本身是可以修改的,但是不可以通过那个指针去修改)
const int *p = &i;
*p = 26; // ERROR!(\*p)是const
i = 26; // OK
p = &j; // OK
转换:
- 总是可以把一个非const的值转换成const的
void f(const int *x); // 表示接受的值,我不会去更改它,你放心!int a = 15;
f(&a); // ok
const int b = a; // 无论传入f的值是不是const都可以
f(&b); // okb = a + 1; // Error
- 当要传递的参数的类型比地址大的时候,这是常用的手段:既能用比较少的字节数传递值给参数,又能避免函数对外面的变量的修改
6. 指针运算
1+1=2?
- 给一个指针加1表示要让指针指向下一个变量
int a[10];
int *p = a;
// *(p+1)——>a[1]
- 如果指针不是指向一片连续分配的空间,如数组就是连续分配的空间,则这种运算没有意义
指针计算:
- 这些算术运算可以对指针做:
- 给指针加、减一个整数(+,+=,-,-=)
- 递增递减(++/–)
指针相减是指相隔了多少个数
*p++
- 取出p所指的那个数据来,完事之后顺便把p移到下一个位置去
- *的优先级虽然高,但是没有++高
- 常用于数组类的连续空间操作
- 在某些CPU上,这可以直接被翻译成一条汇编指令
指针比较:
- <,<=,==,>,>=,!=都可以对指针做(地址大小的比较)
- 比较它们在内存中的地址
- 数组中的单元的地址肯定是线性递增的
0地址
- 当然你的内存中有0地址,但是0地址通常是个不能随便碰的地址
- 操作系统会给每个进程一片虚拟的地址空间,都含有虚拟的从0地址开始的连续空间
- 所以你的指针不应该具有0值
- 因此可以用0地址来表示特殊的事情:
- 返回的指针是无效的
- 指针没有被真正初始化(可以先初始化为0,但是并没有意义的值。可以当你使用这个变量的时候系统会崩溃,表明这个值为0或没有初始化)
- NULL是一个预定定义的符号,表示0地址
- 有的编译器不愿意你用0来表示0地址
指针的类型:
- 无论指向什么类型,所有的指针的大小都是一样的,因为都是地址
- 但是指向不同类型的指针是不能直接相互赋值的
- 这是为了避免用错指针
指针的类型转换:
- void*表示不知道指向什么东西的指针
- 计算时与char*相同(但不相通)
- 指针也可以转换类型
int *p = &i;
void *q = (void*)p;
- 这并没有改变p所指的变量的类型,而是让后人用不同的眼光通过p看它所指的变量
- 我不再当你是int啦,我认为你就是个void!
- 指针的类型就是指针
指针大小和内存的编址方式有关,只是恰好与无符号整形大小相同
他的大小是4字节(32位)就是类似0012ff78(16进制 32位)
注:如果你的电脑是64位电脑那么他的大小就是8字节!- 指针是用来保存内存地址的
内存有按32位编制和按64位编制之分- 为什么要给指针定义类型呢?
只有为指针定义类型
才能知道指针所指向的变量的大小
例如: int*p;和 double*q;
那么读取*p时 就要从地址p开始读取4字节
读取*q时就要从地址q开始读取8字节
用指针来做什么:
- 需要传入较大的数据时用作参数
- 传入数组后对数组做操作
- 函数返回不止一个结果
- 需要用函数来修改不止一个变量
- 动态申请的内存
7. 动态分配内存
malloc:
#include <stdlib.h>
void* malloc(size_t size);
向malloc申请的空间的大小是以字节为单位的
返回的结果是void*,需要类型转换为自己需要的类型
(int*)malloc(n*sizeof(int))
没空间了?
- 如果申请失败则返回0,或者叫做NULL
- 你的系统能给你多大的空间?
#include <stdio.h>
#include <stdlib.h>int main(void) {void *p;int cnt = 0;while (p=malloc(100*1024*1024)) {cnt++;}printf("分配了%d00MB的空间\n", cnt);return 0;
}
free():
- 把申请得来的空间还给“系统”
- 申请过的空间,最终都应该要还
- 出来混,迟早都是要还的
- 只能还申请来的空间的首地址
常见问题:
- 申请了每free ——> 长时间运行内存逐渐下降
- 新手:忘了
- 老手:找不到合适的free的时机
- free过了再free
- 地址变过了,直接去free
BACK:编程之基 --- C语言基础大全 IV
编程之基 --- C语言基础大全 III相关推荐
- 编程之基 --- C语言基础大全 IV
BACK:编程之基 --- C语言基础大全 III 流程一览 Tenth Week 字符串 1. 字符串 2. 字符串函数 Eleventh Week 结构类型 1. 枚举 2. 结构 3. ...
- c语言基础编程题文库,C语言基础编程题资料.doc
C语言基础编程题资料.doc |5-1 求幂之和 15 分本题要求编写程序,计算 sum21 22 23 cdots 2nsum2122232n.可以调用 pow 函数求幂.输入格式输入在一行中给出正 ...
- arcgispython编程案例_ArcGIS Python编程案例(1)-Python语言基础
Python支持大部分在其他语言中出现的编程结构.在本章内容中,我们将会涉及到许多Python支持的编程结构.我们将会首先介绍如何创建一个新的Python脚本以及如何修改已有脚本.之后我们将深入了解P ...
- C语言基础大全(基于千锋教育超详细教程)
C语言基础 1.第一个c语言程序 #include <stdio.h> int main() {printf("hello world");return 0; } [外 ...
- 编程入门:C语言基础知识全网超全不用到处找了!
你背或者不背,干货就在那里,不悲不喜 你学或者不学,编程就在那里,不来不去 听到这话的你是否略感扎心? 01基础知识 计算机系统的主要技术指标与系统配置. 计算机系统.硬件.软件及其相互关系. 微机硬 ...
- c语言 异或_编程入门:C语言基础知识全网超全不用到处找了!(文末附清单)
你背或者不背,干货就在那里,不悲不喜 你学或者不学,编程就在那里,不来不去 听到这话的你是否略感扎心? 01基础知识 1. 计算机系统的主要技术指标与系统配置. 2. 计算机系统.硬件.软件及其相互关 ...
- 【Java编程进阶】Java语言基础入门篇
从今天开始,就要写Java编程进阶从入门到精通系列的文章啦!希望大家学习完都可以找到心仪的工作,在自己热爱的岗位上闪闪发光! 推荐学习专栏:Java 编程进阶之路[从入门到精通] 文章目录 一. 第一 ...
- C语言基础知识入门和C语言入门基础知识大全
一.C语言基础知识入门 C语言一出现,就以其丰富的功能.强大的表达能力.灵活性.方便性和广泛的应用,在世界范围内迅速普及和普及.C语言不仅高效而且可移植.它可以用来开发应用软件.驱动程序.操作系统等. ...
- 2023年C语言基础知识入门(大全)
一.C语言基础知识入门 C语言一经出现就以其功能丰富.表达能力强.灵活方便.应用面广等特点迅速在全世界普及和推广.C语言不但执行效率高而且可移植性好,可以用来开发应用软件.驱动.操作系统等.C语言也是 ...
最新文章
- socket 服务器浏览器与服务器客户端实例
- 使用1个盘三个5G分区创建12G逻辑卷
- 阿里云Elasticsearch的X-Pack:机器学习、安全保障和可视化
- nikon n150在电脑中不显示里面的图片的解决方法
- 一条开启勇士王朝的短信
- Hibernate 异常:“@OneToOne or @ManyToOne on XXX references an unknown entity: XXX”
- 也谈UpdatePanel与UrlRewrite一起work时出现Form Action属性的问题
- Windows解决net Framerwork 3.5无法安装的问题
- linux中cd中文意思,linux中cd ~和!!是什么意思?
- spring静态资源配置
- three 星空穿梭,常见的星空星星移动
- 没有基础可以学画插画吗?怎么学?
- 孙溟㠭创作篆刻作品(稻)纪念袁隆平老先生
- react点击后高亮显示
- 服务器cpu型号后面的字母,Intel 至强 E3服务器CPU后缀解读
- 杰奇linux伪静态,nginx 杰奇 nginx 伪静态规则
- bert-pytorch版源码详细解读
- jsp的实质是什么?
- Guid.NewGuid().ToString()的几种格式
- 关于镜像文件和虚拟光驱
热门文章
- Android监听手机屏幕解锁和判断屏幕状态
- 0G(無線技術最早的拓荒時代)
- 使用 CodeMirror 打造在线代码编辑器
- stm32f1单片机上用FFT测量信号频率(高精度、过程详细)
- 安装、配置Domino Admin V11
- js实现浏览器中的前进、后退按钮
- 移动H2-3获取超管密码
- mxnet编译android,mxnet to ncnn
- python date range_pandas.date_range()用法
- 我国水土流失面积有所减少,水土保持监管能力和现代化水平也显著提升[图]