C_入门常识(1/10)






博文所截笔记均来自:http://c.biancheng.net C语言中文网,并非用于营利,如有侵权,联系删除。





一、以下题目纯粹为了唤醒对C简单语法的记忆


①打印三角形(等了个腰)

#include <stdio.h>
int main()
{
    int i, j, k;
    for(i=1; i<5; i++)
    {
        /* 观察每行的空格数量,补全循环条件 */
        for( j=i ;  j<5 ;  j++ ) //打印本行的空格 
        {
            printf(" ");    //输出空格
        }
        /* 观察每行*号的数量,补全循环条件,
        上面的空格已经打印出来了,
        因此直接在上面的前提下打印*号即可。 */
        for( k=0 ;  k<2*i-1 ;  k++ ) 
        {
            printf("*");   //每行输出的*号
        }
        
        printf("\n");     //每次循环换行,这一句很重要
    }
    return 0;
}


②打印菱形(c++)

#include<iostream>
using namespace std;

void main(){
    // 上三角
	for(int i=1;i<=5;i++){
		for(int j=5;j>=i;j--){
			cout<<" ";
		}
		for(int d=1;d<=2*(i-1)-1;d++){
			cout<<"*";
		}
		cout<<endl;
	}
	// 下三角
	for(int c=1;c<=4;c++){
		for(int j=1;j<=c+1;j++){
			cout<<" ";
		}
		for(int k=4;k>=2*(c-1);k--){
			cout<<"*";
		}
		cout<<endl;
	}
}


③九九乘法表

#include <stdio.h>
int main() 
{ 
    int i, j, result;
    for(i=1;i<=9;i++){
        for(j=1;j<=i;j++){
            printf("%d*%d=%d\t",j,i,j*i);
        }
        printf("\n");
    }
    return 0;
}


④求质数(素数)

#include <stdio.h>
int main()
{
    int m, n;
    for(m=2; m<=50; m++)
    {
		for(n=2; n<m; n++)
        {
            if(    m%n==0    )       //什么条件下跳出当前循环
                break;      //这里应该退出当前循环了
		}	
        if(m == n)   //n循环结束后,如果m=n的话就输出m
            printf("%d  ", m);
	}
	return 0;    
}


⑤1-20之间不能被3整除的数字之和(还是python给力,不过用C和C++来做算法题比较考验功力)

#include <stdio.h>
int main()
{
    int i, sum;
    for(i=1, sum=0; i<=20; i++)
    {
        if(i%3==0)    //能被3整除这个条件如何写呢?
        {
            continue;     //应该用哪个循环结束语句呢?
        }                  
        sum += i;
    }
    printf("sum=%d\n", sum);
    return 0;    
}


⑥分析一个子串在字符串中出现的位置

#include<iostream>
using namespace std;
int main(){
	int i,j,k;
	char s[20]="Today is sunday!",t[10]=" ";
	for(i=0;s[i]!='\0';i++){   // 遍历s,从第一个字符开始分析
		for(j=i,k=0;t[k]!='\0'&&s[j]==t[k];j++,k++);  // 核心代码
		if(t[k]=='\0'){
			cout<<"t在s中的开始位置下标为:"<<i<<endl;    // 注意返回的应该是外层的 i,因为是以i为起始字符
			return i;
		}
	}
	return -1;	// 执行失败
}







二、字符集、字符编码


1、ASCII编码(主要将拉丁文存入计算机)


1-1

① 前面已经讲到,计算机是以二进制的形式来存储数据的,它只认识 0 和 1 两个数字,怎样将文字与二进制对应起来呢?这就需要有一套规范,计算机公司和软件开发者都必须遵守。

② 这样的一套规范就称为字符集(Character Set)或者字符编码(Character Encoding)。

③ 严格来说,字符集和字符编码不是一个概念

  • 字符集定义了字符和二进制的对应关系,为每个字符分配了唯一的编号。可以将字符集理解成一个很大的表格,它列出了所有字符和二进制的对应关系,计算机显示文字或者存储文字,就是一个查表的过程。

  • 而字符编码规定了如何将字符的编号存储到计算机中。如果使用了类似 GB2312 和 GBK 的变长存储方案(不同的字符占用的字节数不一样),那么为了区分一个字符到底使用了几个字节,就不能将字符的编号直接存储到计算机中,字符编号在存储之前必须要经过转换,在读取时还要再逆向转换一次,这套转换方案就叫做字符编码。

④ 字符集为每个字符分配一个唯一的编号,类似于学生的学号,通过编号就能够找到对应的字符。

⑤ 有的字符集在制定时就考虑到了编码的问题,是和编码结合在一起的,例如 ASCII、GB2312、GBK、BIG5 等,所以无论称作字符集还是字符编码都无所谓,也不好区分两者的概念。而有的字符集只管制定字符的编号,至于怎么存储,那是字符编码的事情,Unicode 就是一个典型的例子,它只是定义了全球文字的唯一编号,我们还需要 UTF-8、UTF-16、UTF-32 这几种编码方案将 Unicode 存储到计算机中。

(ASCII 编码已经成了计算机的通用标准,Unicode也是)



1-2

① 标准 ASCII 编码共收录了 128 个字符:其中包含了 33 个控制字符(具有某些特殊功能但是无法显示的字符)和 95 个可显示字符。

② 上表列出的是标准的 ASCII 编码,它共收录了 128 个字符,只用了一个字节中较低的 7 个比特位(Bit)足以表示(27 = 128),所以还会空闲下一个比特位,它就被浪费了。一个字节本应存储最多 28 = 256 个字符。

(英文大写字母的范围是65-90,小写字母的范围是97-122)

③ C语言有时候使用 ASCII 编码,有时候却不是,而是使用后面两节中即将讲到的 GBK 编码和 Unicode 字符集,不要被教材和老师误导。




2、GB2312 和 GBK (存储中文字符)


2-1 为了让本国公民也能使用上计算机,各个国家(地区)也开始效仿 ASCII,开发了自己的字符编码。

① 这些字符编码和 ASCII 一样,只考虑本国的语言文化,不兼容其它国家的文字。

② 这样做的后果就是,一台计算机上必须安装多套字符编码,否则就不能正确地跨国传递数据,例如在中国编写的文本文件,拿到日本的电脑上就无法打开,或者打开后是一堆乱码。

③ 日文、中文、韩文等包含的字符非常多,有成千上万个,一个字节肯定是不够的(一个字节最多存储 28 = 256 个字符),所以要进行扩展,用两个、三个甚至四个字节来表示。

④ 在制定字符编码时还要考虑内存利用率的问题。我们经常使用的字符,其编码值一般都比较小,例如字母和数字都是 ASCII 编码,其编码值不会超过 127,用一个字节存储足以,如果硬要用多个字节存储,就会浪费很多内存空间。

⑤ 为了达到「既能存储本国字符,又能节省内存」的目的,Shift-Jis、Big5、GB2312 等都采用变长的编码方式

  • 对于原来的 ASCII 编码部分,用一个字节存储足以;

  • 对于本国的常用字符(例如汉字、标点符号等),一般用两个字节存储;

  • 对于偏远地区,或者极少使用的字符(例如藏文、蒙古文等),才使用三个甚至四个字节存储。


总起来说,越常用的字符占用的内存越少,越罕见的字符占用的内存越多。


2-2 实际上,中文版 Windows 下的很多程序默认使用的就是 GBK 编码,例如用记事本程序创建一个 txt 文档、在 cmd 或者控制台程序(最常见的C语言程序)中显示汉字、用 Visual Studio 创建的源文件等,使用的都是 GBK 编码。




3、Unicode字符集(将全世界的文字存储到计算机)

3-1

① 各个国家为了自己的语言文化开发的,不具有通用性,在一种编码下开发的软件或者编写的文档,拿到另一种编码下就会失效,必须提前使用程序转码,非常麻烦。
② 人们迫切希望有一种编码能够统一世界各地的字符,计算机只要安装了这一种字编码,就能支持使用世界上所有的文字,再也不需要转码。
③ Unicode 应运而生。Unicode 也称为统一码万国码;看名字就知道,Unicode 希望统一所有国家的字符编码。
④ Unicode 于 1994 年正式公布第一个版本,现在的规模可以容纳 100 多万个符号。

⑤ Windows、Linux、Mac OS 等常见操作系统都已经从底层(内核层面)开始支持 Unicode,大部分的网页和软件也使用 Unicode,Unicode 是大势所趋。

⑥ 不过由于历史原因,目前的计算机仍然安装了 ASCII 编码以及 GB2312、GBK、Big5、Shift-JIS 等地区编码,以支持不使用 Unicode 的软件或者文档。内核在处理字符时,一般会将地区编码先转换为 Unicode,再进行下一步处理。


UTF 是 Unicode Transformation Format 的缩写,意思是“Unicode转换格式”,后面的数字表明至少使用多少个比特位(Bit)来存储字符。


3-2

① 有的编码方式采用 1~n 个字节存储,是变长的,例如 UTF-8、GB2312、GBK 等;如果一个字符使用了这种编码方式,我们就将它称为多字节字符,或者窄字符。
② 有的编码方式是固定长度的,不管字符编号大小,始终采用 n 个字节存储,例如 UTF-32、UTF-16 等;如果一个字符使用了这种编码方式,我们就将它称为宽字符
③ Unicode 字符集可以使用窄字符的方式存储,也可以使用宽字符的方式存储;GB2312、GBK、Shift-JIS 等国家编码一般都使用窄字符的方式存储;ASCII 只有一个字节,无所谓窄字符和宽字符。

④ 注意渗透测试中“宽字节注入”。


JavaScript 本来只能用于 Web 前端,它可以实现一些特效,或者和服务器通信,后来有人把 JavaScript 移植到了服务器上,并起名 Node.js,这样 JavaScript 也能进行 Web 后台开发了。也就是说,只要需要学习 JavaScript 一门语言,就可以搞定网站的前端和后台,成为全栈工程师(所以有不少人说JS终将一同天下,(╯‵□′)╯︵┻━┻)。




4、几句实话

① 对于大部分初学者,学了C语言的基本语法后,发现只能开发“黑底白字”的DOS程序,完全没有漂亮的界面和生动的交互。于是学数据结构,学算法,学操作系统,越陷越深,越来越难,最后迷茫了,不知道学C语言能做什么,认为学习编程很难,开始怀疑自己,甚至想放弃。
② 其实,这是很多初学者都会踩到的一个坑!C语言本身是一门很简单的语言,提供的实用功能不多,大部分要借助操作系统、第三方库、单片机来完成。也就是说,只学C语言基本什么也做不了,也基本找不到工作。

③ 记住,C语言几乎不用来做软件、网站、APP等这些应用层开发,其它的编程语言能够更好地完成任务,没必要非得使用C语言,C语言基本都是用来做底层开发,也就是看不见摸不着的、在后台默默提供服务的那些项目,而这样的项目对初学者来说基本没有实用价值,初学者也不知道它们该怎么使用。

④ C语言的优势是运行效率极高,这正是底层开发所看重的。底层开发有时候就是一个模块,或者是一个服务,规模不算大,但是对效率有严格的要求,此时用C语言就非常合适,所以针对底层开发的C语言库较多,因为它们有非常大的实用价值。


程序员需要很强的自学精神,即使没有老师,没有培训班,也应该一样优秀。






二、语法回忆(Linux中进行实验)


1、运行.c文件方法

①用gcc编译:gcc hello.c  生成 a.out 

② ./a.out

2、各种大小写敏感

3、数据类型及存储大小

用 sizeof() 自己查呗,值范围计算比较简单: -(2的bit次方)——+(2的bit次方)-1

① 标准整数类型:

类型存储大小值范围
char1 byte-128 到 127 或 0 到 255(正负2的7次方)
int2 或 4 bytes-32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647
short2 bytes-32,768 到 32,767
long4 bytes-2,147,483,648 到 2,147,483,647

② 标准浮点类型:

类型存储大小值范围精度
float4 byte1.2E-38 到 3.4E+386 位小数
double8 byte2.3E-308 到 1.7E+30815 位小数
long double10 byte3.4E-4932 到 1.1E+493219 位小数

③ void类型

序号类型与描述
1函数返回为空
C 中有各种函数都不返回值,或者您可以说它们返回空。不返回值的函数的返回类型为空。例如 void exit (int status);
2函数参数为空
C 中有各种函数不接受任何参数。不带参数的函数可以接受一个 void。例如 int rand(void);
3指针指向 void
类型为 void * 的指针代表对象的地址,而不是类型。例如,内存分配函数 void *malloc( size_t size ); 返回指向 void 的指针,可以转换为任何数据类型。

4、变量

extern int d = 3, f = 5;    // d 和 f 的声明 
int d = 3, f = 5;           // 定义并初始化 d 和 f
byte z = 22;                // 定义并初始化 z
char x = 'x';               // 变量 x 的值为 'x'

5、常量

整数常量

整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数:0x 或 0X 表示十六进制,0 表示八进制,不带前缀则默认表示十进制。

整数常量也可以带一个后缀,后缀是 U 和 L 的组合,U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。

212         /* 合法的 */
215u        /* 合法的 */
0xFeeL      /* 合法的 */
078         /* 非法的:8 不是八进制的数字 */
032UU       /* 非法的:不能重复后缀 */
85         /* 十进制 */
0213       /* 八进制 */
0x4b       /* 十六进制 */
30         /* 整数 */
30u        /* 无符号整数 */
30l        /* 长整数 */
30ul       /* 无符号长整数 */

② 浮点常量

3.14159       /* 合法的 */
314159E-5L    /* 合法的 */
510E          /* 非法的:不完整的指数 */
210f          /* 非法的:没有小数或指数 */
.e55          /* 非法的:缺少整数或分数 */

③ 字符常量

④ 字符串常量


6、两种定义常量的方法(常量最好定义为完全大写形式)

  1. ① 使用 #define 预处理器。

  2. ② 使用 const 关键字。

#include <stdio.h>

#define LENGTH 10   
#define WIDTH  5
#define NEWLINE '\n'

int main()
{

   int area;  
  
   area = LENGTH * WIDTH;
   printf("value of area : %d", area);
   printf("%c", NEWLINE);

   return 0;
}


#include <stdio.h>

int main()
{
   const int  LENGTH = 10;
   const int  WIDTH  = 5;
   const char NEWLINE = '\n';
   int area;  
   
   area = LENGTH * WIDTH;
   printf("value of area : %d", area);
   printf("%c", NEWLINE);

   return 0;
}


7、数据在计算机内存中的存储

7-0

内存条是一个非常精密的部件,包含了上亿个电子元器件,元器件很小,几乎是纳米级别。

② 这些元器件,实际上就是电路。

③ 电路的电压会变化,要么是 0V----0表示,要么是 5V----1表示,只有这两种电压。0V 是断电、5V 是通电。

④ 所以,一个元器件有2种状态,0 或者 1。

7-1 通过电路来控制这些元器件的通断电,会得到很多0、1的组合。例如,8个元器件有 28=256 种不同的组合,16个元器件有 216=65536 种不同的组合。虽然一个元器件只能表示2个数值,但是多个结合起来就可以表示很多数值了。

7-2 给每一种组合赋予特定的含义,例如,可以分别用 1101000、00011100、11111111、00000000、01010101、10101010 来表示 C、语、言、中、文、网 这几个字,那么结合起来 1101000 00011100 11111111 00000000 01010101 10101010 就表示”C语言中文网“。

7-3 一般情况下我们不一个一个的使用元器件,而是将8个元器件看做一个单位,即使表示很小的数,例如 1,也需要8个,也就是 00000001。

7-4 1个元器件称为1比特(Bit)或1位,8个元器件称为1字节(Byte),那么16个元器件就是2Byte,32个就是4Byte,以此类推。


总结 在内存中没有abc这样的字符,也没有gif、jpg这样的图片,只有0和1两个数字,计算机也只认识0和1。所以,计算机使用二进制,而不是我们熟悉的十进制,写入内存中的数据,都会被转换成0和1的组合。


8、程序运行的第一步其实是将程序从硬盘中复制到内存中

8-0 比如Windows下使用QQ:

双击QQ图标,操作系统就会知道你要运行这个软件,它会在硬盘中找到你安装的QQ软件,将数据(安装的软件本质上就是很多数据的集合)复制到内存。对!就是复制到内存!QQ不是在硬盘中运行的,而是在内存中运行的。为什么呢?因为内存的读写速度比硬盘快很多。

8-1 为什么内存读写速度更快?

对于读写速度:内存 > 固态硬盘 > 机械硬盘。

机械硬盘是靠电机带动盘片转动来读写数据的,而内存条通过电路来读写数据,电机的转速肯定没有电的传输速度(几乎是光速)快。虽然固态硬盘也是通过电路来读写数据,但是因为与内存的控制方式不一样,速度也不及内存。

8-2 加载器

所以,不管是运行QQ还是编辑Word文档,都是先将硬盘上的数据复制到内存,才能让CPU来处理,这个过程就叫作载入内存(Load into Memory)。完成这个过程需要一个特殊的程序(软件),这个程序就叫做加载器(Loader)。

CPU直接与内存打交道,它会读取内存中的数据进行处理,并将结果保存到内存。如果需要保存到硬盘,才会将内存中的数据复制到硬盘。

例如,打开Word文档,输入一些文字,虽然我们看到的不一样了,但是硬盘中的文档没有改变,新增的文字暂时保存到了内存,Ctrl+S才会保存到硬盘。因为内存断电后会丢失数据,所以如果你编辑完Word文档忘记保存就关机了,那么你将永远无法找回这些内容。


9、至关重要的虚拟内存

如果运行的程序较多,占用的空间就会超过内存(内存条)容量。例如计算机的内存容量为2G,却运行着10个程序,这10个程序共占用3G的空间,也就意味着需要从硬盘复制 3G 的数据到内存,这显然是不可能的。

操作系统(Operating System,简称 OS)为我们解决了这个问题:当程序运行需要的空间大于内存容量时,会将内存中暂时不用的数据再写回硬盘;需要这些数据时再从硬盘中读取,并将另外一部分不用的数据写入硬盘。这样,硬盘中就会有一部分空间用来存放内存中暂时不用的数据。这一部分空间就叫做虚拟内存(Virtual Memory)

3G - 2G = 1G,上面的情况需要在硬盘上分配 1G 的虚拟内存。


硬盘的读写速度比内存慢很多,反复交换数据会消耗很多时间,所以如果你的内存太小,会严重影响计算机的运行速度,甚至会出现”卡死“现象,即使CPU强劲,也不会有大的改观。如果经济条件允许,建议将内存升级为 4G,在 win7、win8、win10 下运行软件就会比较流畅了。


总结:CPU直接从内存中读取数据,处理完成后将结果再写入内存。

happysneaker.com

CPU、内存、硬盘和主板的关系




identifier:标识符









三、杂七杂八


1、C++深拷贝和浅拷贝的区别

① 浅拷贝不复制数据,只复制指向数据的指针,因此是多个指针指向同一份数据。 
② 深拷贝会复制数据,每个指针指向一份独立的数据。

        浅拷贝在构造和删除对象时容易产生问题,因此使用时要十分小心。

        如无必要,尽量不用浅拷贝。

        当我们要传递复杂结构的信息,而又不想产生另一份数据时, 可以使用浅拷贝,比如引用传参。

        浅拷贝特别需要注意的就是析构时的问题, 当多个指针指向同一份内存时,删除这些指针将导致多次释放同一内存而出错。

详解:

位拷贝(浅拷贝)只是对指针的拷贝,拷贝后两个指针指向同一个内存空间;
值拷贝(深拷贝)不但对指针进行拷贝,而且对指针指向的内容进行拷贝(重新分配内存空间),经深拷贝后的指针指向的是两个不同地址的指针(深拷贝采用在堆内存中申请新的空间来存储数据,避免悬垂指针的产生)。


2、

happysneaker.com

happysneaker.com









happysneaker.com

happysneaker.com

happysneaker.com

外部变量extern,对于公用的外部变量可以在任一源文件中定义一次,其他源文件中对其用extern声明后,即可使用。

happysneaker.com

内部函数:只能被本文件中的其他函数所调用的函数称为内部函数。

即:  static 类型 函数名     ——     内部函数也叫做静态函数

在不同文件中可以使用相同名字的内部函数。

happysneaker.com

happysneaker.com

输出单个字符,如下获取单个字符:

happysneaker.com

加上花开括号,可以限定ifelse的配对关系,并非就近原则:

happysneaker.com

3、switch后面的表达式的类型与case后面常量的类型biubiu匹配,而且只能是:

    整型、字符型、枚举型表达式。

happysneaker.com


4、break只能跳出一层循环

5、二维数组

happysneaker.com

happysneaker.comhappysneaker.com


happysneaker.com


6、指针常量:就是变量的内存地址,是一个常量。

     指针变量:就是存放了另一个变量的内存地址。

取地址运算符——&

引用运算符——*

指针变量可以被初始化为0、NULL、任意地址

happysneaker.com

数组的指针:数组的首地址,数组名是常量指针。

happysneaker.com

指针数组:数组中每一个元素都是一个指针,如下,也可以是二维数组的创建。

happysneaker.com

happysneaker.com



7、malloc和堆

happysneaker.com

如果分配成功则返回指向被分配内存的指针,即 malloc 返回的是一个指针。

而 malloc 必须要由我们计算字节数,并且在返回后强行转换为实际类型的指针

如下:

int* p = (int*)malloc(sizeof(int)*128);

//分配128个整型存储单元

//并将这128个连续的整型存储单元的首地址存储到指针变量p中


double* p = (double*)malloc(sizeof(double)*12);

// 分配12个double型存储单元,并将这12个double型存储单元的首地址存储到指针变量p中


int *p = (int*)malloc(sizeof(int) * 100);
//分配可以放得下100个整数的内存空间。


注意事项:

① malloc 函数返回的是 void * 类型。

所以:对于C++,如果你写成:p = malloc (sizeof(int)); 则程序无法通过编译,报错:“不能将 void* 赋值给 int * 类型变量”。

② 函数的实参为 sizeof(int) ,用于指明一个整型数据需要的大小。


8、 详见结构体笔记

① 结构体是一种自定义的数据类型,是创建变量的模板,不占用内存空间;结构体变量才包含了实实在在的数据,需要内存空间来存储。

② 所谓结构体数组,是指数组中的每个元素都是一个结构体。在实际应用中,结构体数组常被用来表示一个拥有相同数据结构的群体,比如一个班的学生、一个车间的职工等。

定义结构体数组和定义结构体变量的方式类似,请看下面的例子:

struct stu{
    char *name;  //姓名
    int num;  //学号
    int age;  //年龄
    char group;  //所在小组 
    float score;  //成绩
}class[5];

表示一个班有5个学生,定义了一个结构体数组来存储这五个学生的信息。

结构体数组在定义的同时也可以初始化,例如:当对数组中所有元素赋值的时候,也可以不给出数组长度 ,比如下面的class[5]就可以省略5:

struct stu{
    char *name;  //姓名
    int num;  //学号
    int age;  //年龄
    char group;  //所在小组 
    float score;  //成绩
}class[5] = {
    {"Li ping", 5, 18, 'C', 145.0},
    {"Zhang ping", 4, 19, 'A', 130.5},
    {"He fang", 1, 18, 'A', 148.5},
    {"Cheng ling", 2, 17, 'F', 139.0},
    {"Wang ming", 3, 17, 'B', 144.5}
};


9、文件操作

happysneaker.com

happysneaker.com

happysneaker.com






Web安全技术分享
请先登录后发表评论
  • 最新评论
  • 总共0条评论