C语言第五节-原码-数组-字符串

原码-反码-补码

数据在计算机内部以补码的形式存储的
数据分为:有符号(正数最高位是0,负数是1)数和无符号数(都是正数
     对于正数:反码==补码==原码
     对于负数:反码==除最高符号位以外的各位取反     补码=反码+1
原码:最高位的符号位+真值
反码:正数→本身
           负数→ 反码==除最高符号位以外的各位取反
为何要引入反码-补码?
补码:使计算机能够做减法 ,简化电路设计1 - 1等价于1 + (-1) 只有补码能实现
           正数→本身
           负数→ 补码=反码+1
          补码→原码:符号位不变,取反+1
//64 位机器侠
   
//int 类型占用 4 个字节,每个字节 8
   
// 计算机存储 使用32 位的二进制数码存储的是补码
   
   
//+1
   
// 原码: 000000000000000000000000000000001
   
// 反码 = 原码
   
// 补码 = 原码
   
   
int b1 = 0 b000000000000000000000000000000001; //0b 表示二进制数 binary
   
printf ( "b1 = %d\n" , b1);
   
   
//-1
   
// 原码: 100000000000000000000000000000001
   
// 反码: 111111111111111111111111111111111
   
//    +000000000000000000000000000000001
   
//     ---------------------------------
   
// 补码: 111111111111111111111111111111111
   
int b2 = 0 b11111111111111111111111111111111;
    printf("b2 = %d\n", b2);

位运算符

位运算符:指按二进制进行的运算(用于整数二进制位之间的运算)
符号:& 按位与:如果两个位进行&操作,同1为1,有0为0,每一位进行比较
          9&5
          00001001
        & 00000101
-------------------
          00000001
     实用:1、任何数&1,只看数的最低位
          2、改变某一位,例如 改变第30位为0
          //...00000100
          //...11111011

     | 按位或:同0为0,有1为1 ,每一位进行比较

     ~ 按位取反:0→1,1→0

     ^ 按位异或:相同为0,不同为1 ,每一位进行比较

     >> 右移位:高位要补原来的符号位,移除部分要舍弃,不会改变正负
               //相当于原数/2^n
     << 左移位: 各二进制位左移N位,高位丢弃,低位补0, 左移可能会改变一个数的正负
//  << 左移位
    //  8 << 2  各二进制位左移 N 位,高位丢弃,低位 0
    //  00000000000000000000000000001000  8
    // →00000000000000000000000000100000  32
    // 相当于原数*2^n   8*2^2

数组(构造类型)

数组:为了处理方便,把具有相同类型的若干变量按有序的形式组织起来,这些按序排列的同类数据的集合
数组元素:构成数组的数据
数组的下标:数组元素的位置的一个索引或指示
存储的内容分类:数值数组、字符数组、指针数组、结构数组
按维度分类:一维数组、二维数组、多维数组(维度跟下标的个数有关

数组元素作为函数的参数,进行数据的传递:1、数组元素作为实参实用(单向值传递)
                                                                           2、数组名作为函数的形参和实参实用

一维数组:所有元素都不是数组(元素为基本数据类型
定义方式:
               类型说明符 数组名[常量表达式];
          常量表达式:表示元素的个数,也是数组的长度
数组的长度:可以使用宏定义
初始化:
     1、定义的同时初始化
          int a[3] = {1,2,3};  //全部初始化
          int b[3] = {1,2};    //部分初始化 ,未写的为0
          int c[ ] = {1,2,3};  //已知元素,可不写数组长度
          int d[10] = {[3]=23, [8]=34};   //下标为3,8的赋初值,其余为0
    2、先定义后初始化,类似变量

数组定义后,不初始化,数组里面有值,值为系统随机的垃圾值,所以正确的使用数组需要初始化

数组的遍历:通过for循环,可以依次访问数组的每一个元素
1、正序输出
          a[n] = {  ..};
          for(int i  = 1;i < n;i++) {
               printf(“%d “, a[i]);
}   
2、倒序输出
           for(int i = n-1; i >= 0; i--){
               printf(“%d “, a[i]);
}

// 从键盘输入一个数组长度,构建一个数组,然后再通过 for 循环从键盘接受数组的数组初始化,并且 for 循环输出查看
   
//1 、从键盘输入长度
   
int length = 0 ;
   
printf ( " 请输入一个长度: " );
   
scanf ( "%d" , &length);
   
//2 、构建数组
   
int a[length];
   
//3 、数组初始化
   
for ( int i = 0 ; i < length; i++) {
       
printf ( " 请输入数组的第 %d 个值: " , i + 1 );
       
scanf ( "%d" , &a[i]);
    }
   
//4 、数组输出
   
for ( int i = 0 ; i < length; i++) {
       
printf ( "%d " , a[i]);
    }

数组的存储
%p:输出地址
存储方式:1、计算机会给数组分配一块连续的存储空间
        2、数组名代表数组的首地址,从首地址开始,依次存入数组元素
        3、每个元素的字节数相同(取决于数组类型)
        4、元素之间地址连续
先定义的变量或数组存在高地址,数组先分配好长度的内存单元,然后依次存储(从低位到高位),数组元素内部连续


一维数组的地址: 数组名代表数组的首地址 a == &a[0] 
一维数组的长度:
     数据类型 a[n];
     数组占用的总字节数:存储n个 * 每个都是sizeof(数据类型) 
     数组长度:数组的总长度 / 数组的任何一个元素在内存中占用的字节数(元素类型)
          length = sizeof(a)/ sizeof(int)

数组越界:编译警告,即使没有报错(版本不一样,可能报错),也是不安全的,越界地址的值有可能丢失

// 通过 for 循环,依次输入 10 个数,保存在数组中,找出最大值
   
   
int a[ 10 ];
   
// 输入 10 个数
   
for ( int i = 0 ; i < 10 ; i++) {
       
       
printf ( " 当前输入第 %d 个数 :" , i + 1 );
       
scanf ( "%d" , &a[i]);
    }
   
// 找出最大值
   
int max = a[ 0 ]; // 假设 a[0] 为最大值 max
   
for ( int i = 1 ; i < 10 ; i++) {
       
if (max < a[i]) {
            max = a[i];
        }
    }
    printf("max = %d\n", max);

数组作为参数
1、数组元素作为实参时,函数的形参不必须是数组(类型 x)  --int x 值传递不改变值
2、数组名作为实参时,函数的形参 必须是数组(类型 arr[])int arr[]  地址传递可改变值
3、数组名作为形参时,长度可以不写
4、数组名作为参数时,数组长度信息会丢失(C中,不管什么类型,数据的内存地址在内存中都是8个字节------数组名就是地址)

数组名作为参数时的注意点:
     形参数组类型和长度要与实参一致,否则会造成错误

冒泡排序

冒泡排序(Bubble Sort):一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来,走访数列的工作时重复地进行,直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来时因为越小的元素会经交换慢慢“浮”到数列的顶端。

冒泡排序分为: 大数下沉  小数上浮

冒泡排序步骤(大数下沉法)


冒泡排序:使用两层循环,外层控制次数,内层逐个比较,n个数比较n-1次,

冒泡排序代码实现
// 冒泡排序
   
   
int a[ 6 ] = { 2 , 5 , 3 , 1 , 7 , 4 };
   
    for ( int i = 0 ; i < sizeof (a) / sizeof ( int ) - 1 ; i++) {
     //-i:因为每一次内外循环结束,都有一个数已经排序好了,-i防止重复比较,优化效率
        for ( int j = 0 ; j < sizeof (a) / sizeof ( int ) - 1 - i; j++) {
           
if (a[j] > a[j+ 1 ]) {
//                int temp = a[j];
//                a[j] = a[j+1];
//                a[j+1] = temp;
                arraySort(a, j);        
         }     
        }
    }
   
   
for ( int i = 0 ; i < sizeof (a) / sizeof ( int ); i++) {
       
printf ( "%d\t" , a[i]);
    }
//数组的交换值
void arraySort( int arr[] , int n) {
   
       
int temp = arr[n];
        arr[n] = arr[n+
1 ];
        arr[n+
1 ] = temp;
   
}

选择排序

选择排序(Selection sort):一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法。

选择排序流程


选择排序代码实现:
// 选择排序:每一趟都找到一个最小或最大数,放在首位
   
int a[ 6 ] = { 3 , 4 , 2 , 10 , 5 , 8 };
 
//    for (int i = 0; i < sizeof(a) / sizeof(int); i++) {
//        for (int j = i + 1; j <= sizeof(a) / sizeof(int) - 1; j++) {
//            if (a[i] > a[j]) {
//                int temp = a[i];
//                a[i] = a[j];
//                a[j] = temp;
//            }
//        }
//    }
   
selectSort (a, 6 );
   
   
   
for ( int i = 0 ; i < 6 ; i++) {
       
printf ( "%d\t" , a[i]);
    }

//选择排序函数封装
//传入数组长度len,因为数组在作为地址传入时,长度会丢失
void selectSort( int arr[], int len) {
   
for ( int i = 0 ; i < len - 1 ; i++) {
       
for ( int j = i + 1 ; j < len; j++) {
           
if (arr[i] > arr[j]) {
               
int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
    }

}

折半查找

折半查找:在一个有序的表中,取中间元素作为查找对象,若给定值与中间元素的要查找得数相等,则成功;若小于,则在中间元素的左半区查找;反之,右半区。不断重复上述步骤,直至结束。

折半查找步骤:
0          1          2          3          4          5
12       45        22        56        40        19
low                                                       high

key = 45
mid = (low + high)/2
循环
     key(45) < a[mid]
          high = mid - 1;

     key(45) > a[mid]
          low = mid +1;

     key(45) == a[mid]
          return mid;

当low > high ,查找不到

折半查找代码实现:
// 折半查找代码实现
   
   
// 数组元素必须有序,才可以折半查找
   
int a[] = { 2 , 3 , 4 , 6 , 10 , 25 , 30 , 40 , 44 };
   
   
// 查找 key = 40
    int location = searchLocation(a, 9, 40);
    printf("location = %d\n", location);

// 折半查找函数实现
//arr 数组   len 数组长度   key 要查找的数
//return 要查找的数的位置, 如果查找不到返回 -1
int searchLocation( int arr[], int len, int key) {
    // 先要定义变量
   
int low = 0 , high = len- 1 , mid;
   
   
// 循环
   
while (low <= high) {

   
// 计算 mid 的位置
    mid = (low + high) /
2 ;
   
   
/*
    
判断 key a[mid] 的关系
    
     key > a[mid]   low = mid +1
     key < a[mid]   high = mid - 1
     key == a[mid]  return mid
     */

   
if (key > arr[mid]) {
        low = mid +
1 ;
    }
else if (key < arr[mid]) {
        high = mid -
1 ;
    }
else {
       
return mid;
    }
   }
   
// 查找不到 return -1
   
return - 1 ;
}

二维数组

二维数组:是特殊的一维数组(一维数组的每个元素又被声明为一维数组)
定义:类型说明符 数组名[常量表达式1][常量表达式2]
a[i][j]:i行 j列
a[3][4]: 3行 4列
 一维数组       一维数组的元素又是一位数组
  a[0]——---a[0][0] a[0][1] a[0][2] a[0][3]
a→a[1]——---a[1][0] a[1][1] a[1][2] a[1][3]
  a[2]——---a[2][0] a[2][1] a[2][2] a[2][3]
     
初始化:类似一维数组
完全初始化:
分段初始化:int [2][3] = {{2, 3, 4}, {7, 5, 6}};
连续赋值:int [2][3] = {2, 3, 4, 7, 5, 6};  计算机根据第二维自动判断
省略第一维:int [][3] = { 2, 3, 4, 7, 5, 6 }
部分初始化:
               int [2][3] = {1};
                    1 0 0
                    0 0 0
               int [2][3] = {{1}, {2}};
                    1 0 0
                    2 0 0

二维数组的遍历
    二维数组的元素称为双下标变量,表示形式: 数组名[第一维下标][第二维下标]
// 遍历二维数组
   
int a[ 2 ][ 3 ] = { 1 , 2 , 3 , 6 , 5 , 4 };
   
   
for ( int i = 0 ; i < 2 ; i++) {
       
for ( int j = 0 ; j < 3 ; j++) {
           
printf ( "%d\t" , a[i][j]);
        }
       
printf ( "\n" );
    }

a[2][2]分析:

int a[ 2 ][ 2 ] = { 1 , 2 , 3 , 4 };
   
// 数组的首地址
   
// 二维数组:数组的首地址 == 数组名 == &a[0] ==&a[0][0]
   
printf ( "a        = %p\n" , a);
   
printf ( "&a[0]    = %p\n" , &a[ 0 ]);
    printf("&a[0][0] = %p\n", &a[0][0]);

二维数组的行数和列数:
// 二维数组占用的总字节数: 1 、总字节数 = 每行占用的字节数之和
   
//                    2 、总字节数 = 元素的个数 * 元素的类型
   
//                              = * * sizeof (数组类型)
   
//                              =sizeof (数组名)
   
int len = 2 * 2 * sizeof ( int );  //16
   
printf ( "%d\n" , len);
    len =
sizeof (a);    //16
    printf("%d\n", len);

    // 每行的字节数如何计算
   
//int[2][2]
   
//a[0]→1   2
   
//a[1]→3   4
    len =
sizeof (a[ 1 ]);  // 8
   
printf ( "%d\n" , len);
   
   
// 每一行有多少列
   
// 列数 = 行的总字节数 / 每个元素占用的字节(数据类型)
    len =
sizeof (a[ 1 ]) / sizeof ( int );  //2
    printf("%d\n", len);

   
// 行数:总字节数 / 每一行占用的字节数
    len =
sizeof (a) / sizeof (a[ 0 ]);  //2
    printf("%d\n", len);

// 有一个 5 X 3 的矩阵,要求编程以求出其中最大的那个元素及其所在的行号和列号
// 算法:将 a[0][0] 作为临时最大值 Max ,然后逐一比较,当 a[i][j] > max, max = a[i][j]
   
   
int a[ 5 ][ 3 ] = {
        {
1 , 3 , 4 },
        {
55 , 2 , 5 },
        {
6 , 9 , 10 },
        {
11 , 15 , 66 },
        {
34 , 22 , 34 }
    };
   
int max = a[ 0 ][ 0 ];
   
int maxRow = - 1 , maxCol = - 1 ;
   
//i: row    j: col
   
for ( int i = 0 ; i < 5 ; i++) {
       
for ( int j = 0 ; j < 3 ; j++) {
           
if (a[i][j] > max) {
                max = a[i][j];
                maxRow = i;
                maxCol = j;
            }
        }
    }
    // 由于数组的下标从 0 开始,所以统计行列应该加 1
    printf("最大值:%d\n在第%d行第%d列!", max, maxRow + 1, maxCol + 1);

二维数组作为函数参数:
     1、数组元素作为函数参数(值传递
     2、数组名作为函数的参数,可以修改值(地址传递
     3、数组名作为某一函数的参数时,同样,该函数里不能计算该数组的字节,长度丢失
     4、数组名作为形参时,可以不写第一维的长度

// 从键盘接受两个参数分别存到 m,n 中,使用 m n 构成数组
//1 、定义一个函数,使用 i, j 初始化数组 a[i][i]
// 定义一个函数打印二维数组的每个值
//scanf() m,n
//int array[m][n]
//init_arr(arr,m,n)
//print_arr(arr,m,n)
   
int m = 0 , n= 0 ;
   
   
printf ( " 请输入两个数 , 确定数组的行数和列数: " );
   
scanf ( "%d %d" , &m, &n);
   
int array[m][n];
   
   
// 传参数,应该先传行列 m n, 不然定义的函数中形参 arr[][] 不知道时几行几列
   
init_arr (m, n, array);
    print_arr(m, n, array);

// 初始化数组
void init_arr( int m, int n , int arr[m][n]) {
   
for ( int i = 0 ; i < m; i++) {
       
for ( int j = 0 ; j < n; j++) {
           
// 给每个元素赋值,使用 i*j
            arr[i][j] = i * j;
        }
    }
}

// 打印数组的值
void print_arr( int m, int n, int arr[m][n]) {
   
for ( int i = 0 ; i < m; i++) {
       
for ( int j = 0 ; j < n; j++) {
           
printf ( "%d\t" , arr[i][j]);
        }
       
printf ( "\n" );
    }
}

字符串:“”

字符串: 位于双引号“”中的字符序列
在内存中以’\0’结束,所占字节比实际多一个
字符串“HELLO”在内存中的存储方式:
     H E L L O \0
C语言中,没有字符串类型的变量,可以用字符数组存储字符串

字符数组
     一维数组:char 数组名[常量表达式]
     二维数组:char 数组名[常量表达式1][常量表达式2]

%s:输入输出一个字符串,直至\0结束
   
// 字符串的输入输出
   
char str[ 10 ] = "hello" ;
   
//%c 打印字符
   
for ( int   i = 0 ; i < 10 ; i++) {
       
printf ( "%c\t" , str[i]);
    }
   
   
//%s 输出一个字符串
   
// 从给定的地址开始,直至 \0 结束
    printf("%s\n", str);

//%s 输入一个字符串
   
char str[ 10 ];
   
scanf ( "%s" , str);
    printf("---->%s",str);
注意:1、如果以%s格式进行输入,注意空格问题。如果输入的字符串有空格,则空格之后的字符无法被接            受保存(scanf遇到空格就结束了)
     2、输入的字符长度要小于数组的长度

字符串结束符\0:
1、char a[] = “abc”;                //系统自动加上\0,所以数组长度必须够存上\0
2、char b[] = {abc\0};//这种写法最好带上\0

字符串长度的计算方法: 计算字符串长度时,不包含\0   ( 字符串占用空间数包含\0)  
1、使用字符串函数strlen(数组名)
2、以\0作为条件判断,遇到\0结束,\0之前的就是字符串长度
     char a[] = “abc”;
     int i = 0; 
      while(a[i] == \0)i++;

// 判断字符串中是否包含某个字符,如果包含,返回其首次出现的位置,否则返回 -1
   
char str1[] = "abcdefghijk" ;
   
int index = searchIndex (str1, 'e' );
    printf("%d\n", index);

// 查找字符的位置
// 传入数组名,待查找字符
int searchIndex( char str[], char key) {
   
// 通过循环依次取得每个字符串的字符
   
// 循环条件 str[i] != '\0'
   
// 如果没有结束,开始比较 key str[i]
   
for ( int i = 0 ; str[i] != '\0' ; i++) {
       
if (str[i] == key) {
           
return i;
        }
    }
   
   
return - 1 ;
}

%s不可以接受空格
字符串处理函数

字符串的函数头文件“string.h”
1、puts():输出一个字符串  stdio.h
          puts(数组名):可以自动换行,可以传地址,必须是字符数组,不可以格式化输出
2、gets():输入一个字符串   stdio.h
          gets(数组名):可以接受空格,存在越界( 不安全
3、strcat():连接一个字符串   “string.h”
          strcat(A,B):A要足够大
4、strcpy():字符串拷贝函数      “string.h”
           strcpy(old,new):new替换old,old要足够大
5、strcmp():字符串比较函数      “string.h”
           strcmp(str1,str2):逐个对应位相比较(ASCII码或字典出现顺序),返回值>0,<0,==0
6、strlen():字符串的长度计算函数      “string.h”
           strlen():不包含\0

// 实现单词首字母大写,并统计单词个数
   
// 定义变量
   
char str[ 100 ];
   
int word = 0 ;
   
int count = 0 ; // 统计单词的个数
   
// 提示用户输入字符串
   
printf ( " 请输入一个字符串: \n" );
   
// 接受字符串
   
gets (str);
   
// 循环取出每一个字符,遇到 \0 循环结束
   
for ( int i = 0 ; str[i] != '\0' ; i++) {
   
   
// 判断
       
if (str[i] == ' ' ) {
           
// 把是否是单词的标记改一下
            word =
0 ; // 这是一个标记, word = 0 表示一个单词
        }
else if (word == 0 ) {
           
// 当前循环 字符时空格   下次循环一定是一个单词
            count++;
            str[i] = str[i] -
32 ; // 减去 32 就是大写
            word =
1 ;    // 表示不是一个单词
        }
    }
   
    printf("单词个数:%d,字符串:%s\n", count, str);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值