[学习][记录] c语言:从放弃到入门(下)

简介: [学习][记录] c语言:从放弃到入门(下)

进阶

数据类型

1.补码

有符号数正负数二进制排列规律:

负数为对应的正数 取反+1

0的补码是0

2. int 和 long ,long long 区别

编译器不同 int和long的含义可能是不同的

而 long long类型是 8个字节,64位

unsigned long long:0~2^64-1

long long:-2^63~ 2^63-1

3.类型转化

显示转化

1>小数据赋给大变量 不会造成数据的丢失,系统为了保证数据的完整性,还提供了符号扩充行为。

2>大数据赋给小变量 会发生截断行为,有可能会造成数据丢失

隐式转化

1>整体提升 32位机中 所有低于32位的整型数据 在运算过程中先要转化为 32 位的

整型数据,然后才参与运算

2>混合提升

(1)有long double 类型参与运算,先全部转化为long double

没有(1)则(2)有double 类型参与运算,先全部转化为double类型

没有(2)则(3)有unsign int 类型参与运算,先全部转化为unsign int类型

没有(3)则(4)有 int 类型参与运算,先全部转化为int类型

3>强制转化

进程空间:

压栈和出栈

每次压栈前会保存当前函数环境包括变量值,然后新创建一块栈区来进行操作。

每次出栈会销毁当前栈顶,最后return到上一个函数所在地址。

一维数组

本质

数组是用于存储相同数据类型数据,且在内存空间连续的一种数据结构类型。数组 三要素。

类型:type [N]

定义:type name[N]

大小:sizeof(type [N]) 或 sizeof(name)

初始化

int array[10] = {1,2,3}; //部分初始化

int array2[10] = {[3] = 10};

int array1[10] = {0}; //清零

int array3[10] = {1,2,3,4,5,6,7,8,9,0,1,2};//越界不检

访问

数组名是数组的唯一标识符,数组的每一个元素都是没有名字的。数据名有两重含 义:

(1)数组名作整体访问

int main(void) {
  int arr[10] = {1,2,3,4,5,6,7,8,9,0};
  //int[10] arr; 
  printf("sizeof(arr) = %d\n",sizeof(arr)); 
  printf("&arr = %p\n",&arr); 
  printf("&arr+1 = %p\n",&arr+1); 
  int(*pa)[10] = &arr; 
  printf("pa = %p\n",pa); 
  printf("pa+1 = %p\n",pa+1); 
  return 0; 
}

输出结果:

sizeof(arr) = 40
&arr = 007AF9DC
&arr+1 = 007AFA04
pa = 007AF9DC
pa+1 = 007AFA04

pa +1 为数组最后一个元素的后的第一个地址

(2)数组名作起始地址访问成员

name[i] == *(name+i) == i[name]
int main(void) {
    int arr[10] = {1,2,3,4,5,6,7,8,9,0};
    printf("arr[0] = %d\n",arr[0]);
    printf("*(arr+0) = %d\n",*(arr+0));
    printf("arr = %p\n",arr);
    printf("arr +1 = %p\n",arr+1);
    printf("&arr[0] = %p\n",&arr[0]);
    printf("&arr[0] +1 = %p\n",&arr[0]+1);
    return 0;
}

输出结果:

arr[0] = 1
*(arr+0) = 1
arr = 010FF7E4
arr +1 = 010FF7E8
&arr[0] = 010FF7E4
&arr[0] +1 = 010FF7E8

作参数传递

参数传递,旨在传递三要素(起始地址,步长, 范围)

void selectSort(int *p,int n)// int 表示步长,p表示起始地址,n表示范围

返回堆中一维数组

返回值返回(一级指针)
char * allocMem(int n) {
 char *p = (char*)malloc(n);
 return p;
}
参数返回(二级指针)
int allocMem(char **p,int n)
{ 
  *p = (char*)malloc(n); 
  return *p== NULL?-1:1;
}

二维数组

本质

二维数组的本质是一维数组,只不过,一维数组的成员又是一个一维数组而己。三 要素:

type name[M][N] == type[N] name[M] 
int arr[3][4] == int[4] arr[3] // 步长为int[4] 起始地址为arr 范围3

初始化

行可以省,列不可以省,部分初始化和清零依然适用

int array[2][3] = {[1][2]=3}; //c99

int array[][3];=> int[3] arry[];//行可以省 列不可以省 行 初始化 自动赋值

访问

数组名,是数组的唯一标识符。

(1)数组名作为整体访问

int main(void) {
    int arr[3];
    //int[3] arr;
    printf("sizeof(arr) = %d sizeof(int[3]) = %d\n",sizeof(arr),sizeof(int[3]));
    printf("&arr = %p\n",&arr);
    printf("&arr+1 = %p\n",&arr+1);
    int array[3][4];
    //int[4] array[3] type array[3]; type[3] array;
    printf("sizeof(array) = %d sizeof(int[3][4]) = %d\n",sizeof(array),sizeof(int[3][4]));
    printf("&array = %p\n",&array);
    printf("&array +1 = %p\n",&array+1);
    return 0;
}

输出结果:

sizeof(arr) = 12 sizeof(int[3]) = 12
&arr = 005CFDF4
&arr+1 = 005CFE00
sizeof(array) = 48 sizeof(int[3][4]) = 48
&array = 005CFDC0
&array +1 = 005CFDF0

&array+1 同一维数组 数组结尾地址

(2)数组名作起始地址访问成员

(3)结论

a 表示第 0 行首地址,a+i 表示第 i 行首地址

*(a+i), a[i], &a[i][0]表示第 i 行第 0 个元素地址 
 *(a+i)+j, a[i]+j, &a[i][j]表示第 i 行第 j 个元素地址 
 *(*(a+i)+j) *(a[i]+j), a[i][j]表示第 i 行第 j 个元素

线性存储

二维数组在逻辑上是二维的,但是在存储上却是一维的。正是这个特点,也可以用 一维数组的方式去访问二维数组的。

int main() { 
   int array[2][3] = {9,8,7,6,5,4}; 
   for(int i=0;i<2; i++) {
    for(int j=0;j<3;j++) {
     printf("%d ",array[i][j]); 
    }
    putchar(10); 
    }
    int *p = (int *)array; 
    for(int i=0; i<6;i++) { 
    printf("%d ",p[i]); 
    }
    return 0; 
}

参数传递

二维数组本质是常量数组指针,所以跟其对应的形参也应该是数组指针类型的变 量

void displayArray(int(*p)[4],int n) //步长int[4] 首地址p,范围n

数组指针

一次可以移动一个字节指针,称为 char 类型的指针,
一次可以移动二个字节的指 针,称为 short
类型的指针。

一次可以移动一个数组大小的指针,是什么类型的指针的,又该如何称谓呢?

比如 二维数组名: arr[3][4]; arr+1 一次加 1 的大小,就是 int[4]类型的大小,即一个数组 的大小。

定义

int [N] *pName; =>int (*pName)[N];

语法解析:“()”的优先级比“[]”高, “*”号和 pName 构成一个指针的定义, 指针的类型为 int [N]。

别名

typedef int (*TYPE)[N];
TYPE a;//等价于 int (*a)[N];

数组指针与数组名

解析 一维数组名 二维数组名
示例 int arr[4] int arr[3][4]
本质 一级指针 数组指针
引用 &arr 数组指针 &arr 数组指针
应用

(1)二维数组传参

void displayArray(int(*p)[4],int n)
• 1

(2)一维空间的二维访问

int main() {
    int arr[12] = {1,2,3,4,5,6,7,8,9,10,11,12};
    int (*p)[2] = (int(*)[2])arr;
    for(int i=0; i< sizeof(arr)/sizeof(int[2]);i++) {
        for(int j=0; j<2; j++) {
            printf("%d\t",p[i][j]);
        }
        putchar(10);
    }
}

输出结果:

1 2

3 4

5 6

7 8

9 10

11 12

多维指针

本质分析

type name[x][y][z] == type [y][z] name[x]; //步长为 type[][],首地址name,范围 x

指针

编址

变量地址

对变量取地址,取得是最低位字节的地址。32 位机下,大小均为 4。

int main(void) {
    char a;
    short b;
    int c;
    printf("&a = %p\n",&a);
    printf("&b = %p\n",&b);
    printf("&c = %p\n",&c);
    printf("sizeof(&a) = %d\n",sizeof(&a));
    printf("sizeof(&b) = %d\n",sizeof(&b));
    printf("sizeof(&c) = %d\n",sizeof(&c));
    return 0;
}

输出结果:

&a = 00CFF897
&b = 00CFF890
&c = 00CFF88C
sizeof(&a) = 4
sizeof(&b) = 4
sizeof(&c) = 4

本质

指针的本质,就一个有类型的地址。

int main(void) {
    int a = 0x12345678;
    printf("%p\n",&a);
    printf("%d\n",*(&a));
    //printf("%d\n",a);
    //printf("%x\n",*((int*)0x0060FEAC));
    return 0;
}

指针变量

内存的地址,即指针(常量),存放该地址的变量就是指针变量。此变量,必须满足 3 个条件,大小为 4,有类型,区别于其它变量。

type *var;

type 决定了类型(步长),* 表示该变量是指针,var 用于存储地址。

变量大小

指针变量 大小始终为4字节

数组变量 大小为 类型(即步长)* 范围 字节

int main(void) {
    char *pa;
    int *pi;
    printf("sizeof(pa) = %d sizeof(pb) = %d\n", sizeof(pa),sizeof(pi));
    printf("sizeof(char*) = %d,sizeof(int*) = %d\n",sizeof(char*),sizeof(int*));
    char *pm = (char*)malloc(100);
    printf("sizeof(pm) = %d\n",sizeof(pm));
    int (*parr)[10];
    printf("sizeof(parr) = %d\n",sizeof(parr));
    int arr[100];
    printf("sizeof(arr) = %d\n",sizeof(arr));
    printf("sizeof(&arr) = %d\n",sizeof(&arr));
    return 0;
}

输出结果:

sizeof(pa) = 4 sizeof(pb) = 4
sizeof(char*) = 4,sizeof(int*) = 4
sizeof(pm) = 4
sizeof(parr) = 4
sizeof(arr) = 400
sizeof(&arr) = 4
变量类型

类型,决定了,从 var 存放的地址开始的寻址能力。

int main(void) {
    int a = 0x12345678;
    char *pa = &a;
    printf("%x\n",*pa);
    short *ps = &a;
    printf("%x\n",*ps);
    int *pi = &a;
    printf("%x\n",*pi);
    return 0;
}

输出结果:

78
5678
12345678

注意:值的存储 由低到高

引用与解引用
int main(void) {
    int a = 0x12345678;
    printf("%x\n",*&a);
    int arr[3];
    printf("arr = %p\n",arr);
    printf("&arr = %p\n",&arr);
    printf("arr+1 = %p\n",arr+1);
    printf("&arr+1 = %p\n",&arr+1);
    printf("*&arr = %p\n",*&arr);
    printf("*&arr + 1 = %p\n",*&arr+1);
    return 0;
}

输出结果:

12345678
arr = 006FFAC4
&arr = 006FFAC4
arr+1 = 006FFAC8
&arr+1 = 006FFAD0
*&arr = 006FFAC4
*&arr + 1 = 006FFAC8

在地址 方面,

数组arr => &arr ,也有arr => *&arr

但arr+1 !=> &arr+1 (步长不一样)

运算

指针的运算是,地址值+类型的运算。

int main(void) {
    int a[10];
    printf("a = %p\n",a);
    printf("a+1 = %p\n",a+1);
    printf("&a[9] - &a[4] = %d\n",&a[9] - &a[4]);
    printf("(int)&a[9]-(int)&a[4] = %d\n",(int)&a[9] -(int)&a[4]);
    return 0;
}

输出结果:

a = 0116FAE8
a+1 = 0116FAEC
&a[9] - &a[4] = 5
(int)&a[9]-(int)&a[4] = 20

解释几点:

a和a+1是 运算是以步长(此时为int)为单位 +1即移动一个Int长度

&a[9],&a[5]都是转为指针int* 也是以int为步长单位的,所以9-5+1=5步

(int)&a[9],(int)&a[4] 被强转为字节为单位的了 所以结果为 5*4 =20字节

补充几个例子:

例1

int main() {
    int a[5] = {1,2,3,4,5};
    int *ptr1 = (int *)(&a + 1);//此时ptr1指向a末尾后面一个int
    int *ptr2 = (int *)((int)a + 1);//此时ptr2指向a起始地址后面一个字节
    printf("%x, %x",ptr1[-1], *ptr2);//ptr前移一个int 即a[5],ptr2取一个int长度包含了a[0]后三个字节和a[1]第一个字节
    return 0;
}

输出结果:

5, 2000000

解释

int *ptr1 = (int *)(&a + 1);//此时ptr1指向a末尾后面一个int

int *ptr2 = (int *)((int)a + 1);//此时ptr2指向a起始地址后面一个字节

printf("%x, %x",ptr1[-1], *ptr2);//ptr前移一个int 即a[5],ptr2取一个int长度包含了a[0]后三个字节和a[1]第一个字节

例2

int main(int argc, char **argv) {
    int a;
    int *p = &a;
    printf("%x, %x\n",p,p+1);
    printf("%x, %x",(int)p,(int)p+1);
    //printf("%x, %x",(void*)p,(void*)p+1);
    return 0;
}

输出结果:

84fd44, 84fd48

84fd44, 84fd45

同理,(int *)和(int)步长区别

二级指针

定义:

二级指针,是一种指向指针的指针。我们可以通过它实现间接访问数据,和改变一级

指针的指向问题。

初始化:

二级指针 实际上存储着一级指针的地址

作用:间接数据访问

(1)改变一级指针指向的内容

(2)改变一级指针指向

同理 N级指针 是为了间接访问N-1级指针数据

FILE与 sqlite3 函数设计的比较:

FILE* file = fopen(filePathStr,modeStr); // 返回句柄 不利于错误判断

if()file == NULL) return -1;

sqlite3 *db = NULL;

int rc = sqlite_open(&db,sql,NULL,NULL); //错误码 和句柄分开返回

if(rc == SUCCESS)

FILE 和 sqlite3 均是描述资源的句柄(即一个结构体)。获取文件句柄的方式是通过

返回值,而获取数据库句柄的方式是参数。

FILE *的设计方式,并不是很好。原因是,返回值中要容错出错码。而最合理的

方式,是将出错码和返回值分开。

步长

步长为一级指针长度 即4 bytes

二级指针和指针数组

const修饰符修饰指针

作用增强程序的键壮性。但凡被修饰过的变量,其值不可修改

const 修饰指针

const 修饰变量

const 修饰变量,此时称为常变量。常变量,有常量的属性,比用宏定义的常量有 了类型的属性

特点:声明时就必须定义,且定义之后不可修改。且发生在编译时期。

可以通过指针修改const修饰的局部变量,但不可以修改const全局变量

const int b = 100;
int main(void)
{
  const int a = 100;
  printf("temp a = %d\n", a);
  int *pa = &a;
  *pa = 300;
  printf("after temp a = %d\n",a);
  int *pb = &b;
  printf("global b = %d\n", b);
  pb = 250;
  printf("after global  b = %d\n", b);
  system("pause");
  return 0;
}
输出结果:
temp a = 100
after temp a = 300
global b = 100
after global  b = 100
请按任意键继续. . 

const 修饰符

(1) const 修饰指针,表示指针的指向是恒定的,不可更改。

int * const p = &a;

const修饰p,指针p是不可变的,也就是p指向的内存单元不可变。即p的指向不可变,p指向的内存单元的内容可以变。


(2) const 修饰指针指向的值 不能改变

const int *p = a;

a的值不能改变

(3) const int* const p = &a;

*p和p都被const修饰了,所以p指向的内存单元,和p指向内存单元中存放的内容都是不可变的。

函数指针与回调函数

函数的本质

函数的本质是一段可执行性代码段。函数名,则是指向这段代码段的首地址。

回调函数

回调(函数作参数)

回调函数,本质也是一种函数调用,先将函数以指针的方式传入,然后,调用。这种写法的好处是,对外提供函数类型,而不是函数定义。这样我们只需要依据函数类型 和函数功能提供函数就可以了。给程序的书写带来了很大的自由。

练习

解释((void() ()) 0)();

地址为0的函数强转为(void(*)())函数的调用



相关文章
|
1月前
|
安全 编译器 C语言
C++入门1——从C语言到C++的过渡
C++入门1——从C语言到C++的过渡
59 2
|
1月前
|
存储 Java 编译器
初识C语言1——C语言入门介绍
初识C语言1——C语言入门介绍
30 1
|
1月前
|
C语言
教你快速理解学习C语言的循环与分支
教你快速理解学习C语言的循环与分支
17 0
|
1月前
|
C语言
回溯入门题,数据所有排列方式(c语言)
回溯入门题,数据所有排列方式(c语言)
|
3月前
|
C语言
C语言------程设设计入门
这篇文章是C语言程序设计的入门教程,涵盖了C程序的实现过程、VC集成开发环境的使用、基本数据类型的使用、格式控制字符的作用,以及通过示例代码演示了如何使用printf()函数输出不同类型的数据。
C语言------程设设计入门
|
4月前
|
存储 Java C语言
【C语言入门】初识C语言:掌握编程的基石
【C语言入门】初识C语言:掌握编程的基石
67 4
【C语言入门】初识C语言:掌握编程的基石
|
3月前
|
NoSQL Java 编译器
C语言从入门到精通该怎样学?
持续学习与实践:编程是一门需要不断学习和实践的技能,要保持对新技术和新知识的敏感性,并持续进行编程实践。
58 1
|
3月前
|
编译器 C语言
C语言函数的学习
掌握函数的使用是学习C语言的关键一环,理解和应用这些基本的函数概念将使你能够更有效地利用C语言的强大功能。
24 0
|
4月前
|
存储 Java 程序员
【C语言入门】C语言入门:探索编程世界的基础概念
【C语言入门】C语言入门:探索编程世界的基础概念
108 2
|
4月前
|
前端开发 C语言 C++
C语言入门02---环境搭建
C语言入门02---环境搭建