当前位置:   article > 正文

【2024届_嵌入式_C_C++笔试面试总结】_一个进程需要访问整型数组 a[256][256](每个数字2b)的如下数据,请计算如果置换算

一个进程需要访问整型数组 a[256][256](每个数字2b)的如下数据,请计算如果置换算

C

移除元素

/*
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1: 给定 nums = [3,2,2,3], val = 3, 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。 你不需要考虑数组中超出新长度后面的元素。

示例 2: 给定 nums = [0,1,2,2,3,0,4,2], val = 2, 函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。

你不需要考虑数组中超出新长度后面的元素。
*/

方法一(暴力解法)

#include<stdio.h>
int removeElement(int arr[],int size, int val)
{
  int newSize = size;
  for(int i = 0; i<newSize; i++)
  {

    if(arr[i] == val)//发现需要移出的元素,整体元素向前移动
    {
      for(int j = i+1; j<size; j++)
      {
        arr[j-1] = arr[j];  //往前移动
      }
      i--;
      newSize--;
    }
  }
  return newSize;
}

int main()
{
  int arr[] = {1,2,2,3,4,3,5};
  int val = 3;
  int size = sizeof(arr) / sizeof(int);
  int newsize = removeElement(arr,size,val);
  printf("大小%d\n",newsize);
  printf("移除后的数组\n");
  for(int i = 0; i<newsize; i++)
  {
    printf("%d ",arr[i]);
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

方法二 快慢指针

/*****************************************/
//双指针   
//快指针用来获取新数组中的元素
//慢指针时获取新数组中需要更新的位置
/*****************************************/
#include<stdio.h>
int yichuyuansu(int *arr, int size,int val){
  int slow = 0,fast;
  for(fast = 0; fast<size; fast++){
    //更新新数组元素的值
    if(arr[fast]!=val){//找到不等于val的值,然后更新slow
      arr[slow] = arr[fast];
      slow++;
    }
  }
  return slow;//slow就是最后新数组的大小
}

int main()
{
  int arr[] = {1,2,2,3,4,3,5};
  int val = 3;
  int size = sizeof(arr) / sizeof(int);
  int newsize = yichuyuansu(arr,size,val);
  printf("移除后的大小%d\n",newsize);
  printf("移除后的数组\n");
  for(int i = 0; i<newsize; i++)
  {
    printf("%d ",arr[i]);
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

素数

//输出 100 到 200 以内的所有素数
/*素数是除了1和他本身没有其他因子的整数,如果从2到n-1,都不存在能被n整除的数,那么
(n % i == 0, 2<=i<= n-1)该数就是素数,否则就不是素数*/
#include<stdio.h>
int sushu(int num)
{
  int flag = 1;
  for(int i=2;i<num;i++)
  {
    if(num % i == 0)
    {
      flag = 0;
    }
  }
  return flag;
}

int main()
{
  for(int i=100;i<200;i++)
  {
    if(sushu(i))
    {
      printf("%d ",i);
    }
  }
  return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

水仙花数

/*水仙花数:一个三位数,每位的立方,求和若等于这个三位数,则他就是水仙数*/
#include<stdio.h>

int Narcissus(int num)
{
  int bai = num / 100;      //取出百位
  int shi = num / 10 % 10;  //取出十位
  int ge  = num % 10;       //取出个位
  if(num == (bai*bai*bai + shi*shi*shi + ge*ge*ge))
  {
    return 1;
  }
  else{
    return 0;
  }
}

int main()
{
  // printf("请输入一个三位数:");
  // int num = 0;
  // scanf("%d",&num);
  // printf("%s",Narcissus(num)?"是水仙花数":"不是水仙花数");
  // return 0;

  printf("找出从100到999所有的水仙花数并打印");
  for(int i = 100; i<999; i++){
    if(Narcissus(i)){
      printf("%d ",i);
    }
  }

  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

判断一个正整数是否为回数

注意的是,i初始化为0,即int i = 0;否则只是int i,可能i的初始化不是0,导致最终结果是其他数。

// /*写一个函数实现检测一个正整数是否是回数,如果是,返回1,不是返回0.如12321,就是一个回数*/

#include <stdio.h>
int checkhuishu(int num){
  int temp = num,i=0,j;
  char buff[10] = {0};
  while(temp != 0)
  {
    buff[i] = temp % 10;  //获取最后一个元素
    temp = temp / 10;  //更新num
    i++;
  }
  int flag = 1;
  for(j=0;j<i/2;j++)
  {
    if(buff[j] != buff[i-1-j])
    {
      flag = 0;
      break;
    }
  }
  return flag;
}

int main()
{

  int num;
  printf("请输入需要判断的整数: ");
  scanf("%d",&num);
  printf("%s\n",checkhuishu(num)?"回数":"不是回数");
  // if(checkhuishu(num))
  // {
  //   printf("%d是回数",num);
  // }
  // else{
  //   printf("%d不是回数",num);
  // }
  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

排序

快速排序

它是从冒泡排序演变而来的,但是比冒泡高效。

选择排序

冒泡排序

冒泡排序法是把N个数通过N-1轮排序,升序排序中大的数往下沉,小的数往上浮;降序排序中小的数往下沉,大的数往上浮。

#include<stdio.h>
/*
冒泡排序的特点:升序排序中每一轮比较会把最大的数下沉到最底,所以相互比较的次数每一轮都会比前一轮少一次
*/
int maopao_sort(int *a, int len){
  int i,j,temp;
  for(i =0 ;i<len-1; i++){ //比较几轮
    for(j=0;j<len-1-i;j++){//每轮比较几次
      if(a[j] > a[j+1])
      {
        temp = a[j];
        a[j] = a[j+1];
        a[j+1] = temp;
      }
    }
  }
}

int main()
{
  int arr[8] = {21,12,33,22,1,2,1,4};
  maopao_sort(arr,8);
  printf("排序后:\n");
  for(int i = 0;i<8; i++){
    printf("%d ",arr[i]);
  }
  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

请写出判断程序栈增长方向的程序

https://blog.csdn.net/weixin_45030965/article/details/124451530

我们千万不能假设一个函数内的局部变量先定义的先入栈,这与编译器的实现有关,为了让我们判断栈增长的方向具有可移植性,肯定不能用上述方法判断。

那我们又换一个方法:函数之间调用,先调用的函数信息肯定先入栈,后调用的函数其信息后入栈。

fun1()
{
}

fun()
{
	fun1();
}

main()
{
	fun();
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在这里插入图片描述

#include<stdio.h>

int fun1(int *a)
{
  int b = 2;
  if(a > &b)
  {
    printf("stack 向下增长");
  }else{
    printf("stack 向上增长");
  }
}

void fun()
{
  int a = 1;
  fun1(&a);
}

int main()
{
  fun();
  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

分别给出bool,int,float,指针变量 与“零值”比较的 if 语句(假设变量名为var)

	Bool: if(!var)
	Int : if(var==0)
	Float:  const float val=0.00000001
			If((var >= -val) && (var <= val))  //fabs(val) <= val;


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

如何判断一个字节数据中有多少bit为1

#include<stdio.h>

int select(unsigned char data){
  int count = 0, i=1;
  while(data != 0){
    count += (data & i);
    data >>= 1;  //从低位计算bit是否为1,计算完后,整体数据右移,再次计算下一位是否bit为1
  }
  return count;
}

int main(){
  unsigned char data = 0b11010010;
  int result = select(data);
  printf("Number of 1s in %02X is %d\n",data,result);
  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

在这里插入图片描述

strcat自定义函数实现

#include<stdio.h>

char *my_strcat(char *des, const char *src){
  char *temp = des;
  //assert(des!=NULL && src != NULL);
  while(*des != '\0')
  {
    des++;
  }
  while((*des++ = *src++) != '\0' )
  {};
  return temp;
}

int main()
{
  char src[50],des[50];

  my_strcat(src,"This is source");
  my_strcat(des,"this is destination");

  my_strcat(des,src);

  printf("最终的目标字符串:|%s|",des);

  return 0;
}


  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

二分查找法

#include<stdio.h>

int search_binary(int *a,int len,int target)
{
  int left = 0;
  int right = len - 1;
  while(left<=right)
  {
    int middle = left + (right-left) / 2;
    if(target > a[middle])
    {
      left = middle+1;
    }
    if(target<a[middle])
    {
      right = middle - 1;
    }
    if(target == a[middle])
    {
      return middle;
    }
  }
  return -1;
}

int main(void)
{
  int a[] = {1,2,3,23,55,110};
  int len = sizeof(a)/sizeof(int);
  int target = 2;
  int find = search_binary(a,len,target);
  if(find != -1)
  {
    printf("找到了,位置为%d",find+1);
  }
  else{
    printf("没找到");
  }
  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

判断一个字符串是否为回文字符串

#include <stdio.h>
#include <string.h>

int is_palindrome(char *input_str) {
    int left = 0;
    int right = strlen(input_str) - 1;
    
    while (left < right) {
        if (input_str[left] != input_str[right]) {
            return 0; // 不是回文
        }
        left++;
        right--;
    }
    
    return 1; // 是回文
}

int main() {
    char input_str[100];
    
    printf("请输入一个字符串: ");
    scanf("%s", input_str);
    
    if (is_palindrome(input_str)) {
        printf("是回文\n");
    } else {
        printf("不是回文\n");
    }
    
    return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
#include<stdio.h>
#include<string.h>

/*判断输入的字符串是否为回文字符串*/
int charStrJudge(char* str){
  int len = strlen(str);
  for(int i = 0; i<len/2; i++){
    if(str[i] != str[len-i-1]){
      return 0;
    }
  }
  return 1;
}

int main()
{
  char str[100];
  scanf("%s",str);
  if(charStrJudge(str))
  {
    printf("%s是回文字符串",str);
  }
  else{
    printf("%s不是回文字符串",str);
  }
  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

平均值——去掉最大最小

#include<stdio.h>
/*编写一个函数,实现将数组中最大最小值去掉,剩余成员求平均值*/
void removeMinMaxAndAverage(int arr[], int size){
  if(size < 3)
  {
    printf("数组长度必须大于等于3\n");
    return;
  }

  int min = arr[0];
  int max = arr[0];
  int sum = 0;

  //找到最大值和最小值
  for(int i = 0; i<size; i++){
    if(arr[i] < min){
      min = arr[i];
    }
    if(arr[i] > max){
      max = arr[i];
    }
    sum += arr[i];
  }
  //计算剩余元素个数的总和
  int remaining_sum = sum - min - max;
  int remaining_count = size - 2;

  //计算平均值
  double average  = (double)remaining_sum / remaining_count;

  printf("将数组中最大最小值去掉,剩余成员求平均值%.2f\n",average);
}

int main()
{
  int arr[] = {1,2,3,4,4,3,2,1};
  int size = sizeof(arr) / sizeof(arr[0]);

  removeMinMaxAndAverage(arr,size);

  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

字符串压缩20230911

代码

/*字符串压缩*/
#include <stdio.h>
#include <string.h>
#define lenth 20

void compress(char* p)
{
  if(p == NULL)
  {
    return;
  }
  if(*p == NULL)
  {
    return;
  }

  int count = 1;  //用于计算一个字符出现的次数

  while(*p != NULL)
  {
    if(*p == *(p+1))
    {
      count++;
      p++;
    }
    else{
      printf("%c%d",*p,count);
      count = 1;//重新置1,统计下一个不同的字符
      p++;
    }
  }
}

int main()
{
  char s[lenth];

  printf("please input a string\n");
  scanf("%s",&s);

  compress(s);

  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

结果演示

在这里插入图片描述

C语言的字母大小写转换

#include<stdio.h>
//在C语言中,字符是以ASCII码形式存储的。
//小写字母 'a' 对应的ASCII码是 97,大写字母 'A' 对应的ASCII码是 65。
void swapdaxiaoxie(char* letter)
{
  int i = 0;
  while(letter[i] != '\0')
  {
    if(letter[i] >= 'a' && letter[i] <= 'z' )
    {
      letter[i] = letter[i] - 32;
    }
    else if(letter[i] >= 'A' && letter[i] <= 'Z')
    {
      letter[i] = letter[i] + 32;
    }
    i++;
  }
}

int main()
{
  char s[100];
  printf("请输入一个字母字符串:");
  scanf("%s",s);

  printf("交换前:");
  printf("%s",s);

  swapdaxiaoxie(s);

  printf("交换后: ");
  printf("%s",s);
  
  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

计算字符串长度

#include<stdio.h>
#define length 100
//计算字符串长度不包括'\0'
void stringLength(char *zifuchuang)
{
  int i = 0;
  int count = 0;
  while(zifuchuang[i] != '\0')
  {
    count +=1;
    i++;
  }
  printf("长度为:%d",count);
}

int main()
{
  printf("请输入一个字符串,计算他的长度");
  char s[length];
  scanf("%s",s);

  stringLength(s);

  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
#include<stdio.h>
int stringLength(char * str)
{
	int count = 0;
	while(*str != '\0' )
	{
		count++;
		str++;
	}
	return count;
}

int main()
{
	char str[] = "abafgfdg fdsaf";
	int len = stringLength(str);
	printf("字符串的长度是:%d",len);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

手写整型和字符串的转换函数

字符串转整数

#include<stdio.h>

int my_atoi(char *str)
{
	int sum=0,status=1;
  //如果传入的字符串指针是空指针(NULL),则直接返回0,表示转换失败
	if(str == NULL)
	{
		return 0;
	}
  //负数
  //如果字符串的第一个字符是负号('-'),则将 status 设置为-1,表示接下来要处理负数,并将指针 str 向后移动一位。
	if(*str == '-')
	{
		status = -1;
		str++;
	}
	while((*str)!='\0')
	{
		sum=sum*10+ ((*str)-'0');
		str++;
	}
	return sum*status;
}

int main(){
  char str[] = "12345";
    int result = my_atoi(str);

    printf("The result is: %d\n", result);

    return 0;
}
  /*
    (*str) - '0'表示什么
ChatGPT
(*str) - '0' 表达式实际上是将字符型数字转换为对应的整数值。

在ASCII码表中,数字字符 '0' 到 '9' 的编码是连续的,依次为 48 到 57。假设 *str 是一个数字字符,那么它的ASCII码值减去字符 '0' 的ASCII码值,就得到了对应数字的整数值。

举个例子,假设 *str 的值是字符 '5',那么:

ASCII码值('5') = 53
ASCII码值('0') = 48
所以 (*str) - '0' 的值就是 53 - 48 = 5,也就是字符 '5' 所对应的整数值。

这种转换方法在将字符型数字转换为相应的整数值时非常常见。
    */
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

手写字符串乘法

链表类

链表内指定区间翻转

在这里插入图片描述

代码

https://www.nowcoder.com/practice/b58434e200a648c589ca2063f1faf58c?tpId=295&tqId=654&ru=/exam/oj&qru=/ta/format-top101/question-ranking&sourceUrl=%2Fexam%2Foj

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param head ListNode类 
 * @param m int整型 
 * @param n int整型 
 * @return ListNode类
 */
struct ListNode* reverseBetween(struct ListNode* head, int m, int n ) {
    // write code here
    //如果头节点为空或者头节点的下一个节点为空,那么直接返回头节点
    if(head->next ==NULL || head == NULL){
        return head;
    }
    //创建虚拟头节点
    struct ListNode* dummy_head = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* left,*right,*temp1,*pre,*temp3;
    dummy_head->next = head;
    left = dummy_head;
    right = dummy_head;
    //定位到m-1个点,因为需要造作m
    for(int i = 0; i<m-1; i++){  //移动到第m-1个节点
        if(left == NULL)
        {
            return NULL;
        }
        left = left->next;
    }
    temp1 = left->next;  //保存第m个节点
    for(int i = 0; i<n; i++){  //移动到第n个节点
        if(right == NULL){
            return NULL;
        }
        right = right->next;
    }
    pre = right->next;  //保存第n个节点的下一个节点
    //开始局部翻转
    while(temp1 != right){
    
        temp3 = temp1->next;  //保存temp1->next节点,准备换向
        temp1->next = pre;    //第m个节点的下一个节点就是n+1(第一轮)
    
        pre = temp1;          //让pre指向temp1
        temp1 = temp3;        //移动到下一个节点
    }
    //第n个节点的下一个节点为pre,换向
    temp1->next = pre;
    //第m-1节点的下一个节点为右节点
    left->next = right;
    return dummy_head->next;  //返回虚拟头节点的下一个节点
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

两个链表的第一个公共节点

在这里插入图片描述

代码

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */

/**
 * 
 * @param pHead1 ListNode类 
 * @param pHead2 ListNode类 
 * @return ListNode类
 */
struct ListNode* FindFirstCommonNode(struct ListNode* pHead1, struct ListNode* pHead2 ) {
    // write code here
    int len_a = 0;  
    int len_b = 0;
    struct ListNode* cur_A = pHead1;
    struct ListNode* cur_B = pHead2;
    while(cur_A!=NULL){  //求链表A的长度
        cur_A = cur_A->next;
        len_a++;
    }
    while(cur_B!=NULL){  //求链表B的长度
        cur_B = cur_B->next;
        len_b++;
    }
    cur_A = pHead1;
    cur_B = pHead2;
    if(len_a > len_b){   //如果A比B长,则让A先移动dif的长度,然后让两者同时向下遍历,如果相等,则有公共节点
        int len_dif = len_a - len_b;
        for(int i = 0; i<len_dif; i++){
            cur_A = cur_A->next;
        }
        while(cur_A != NULL){
            if(cur_A == cur_B){
                return cur_A;
            }
            cur_A = cur_A->next;
            cur_B = cur_B->next;
        }
    }
    if(len_a <= len_b){  //如果B比A长,则让B先移动dif的长度,然后让两者同时向下遍历,如果相等,则有公共节点
        int len_dif = len_b - len_a;
        for(int i = 0; i<len_dif; i++){
            cur_B = cur_B->next;
        }
        while(cur_A != NULL){
            if(cur_A == cur_B){
                return cur_A;
            }
            cur_A = cur_A->next;
            cur_B = cur_B->next;
        }
    }
    return NULL;         //没有公共节点则返回空
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57

删除有序链表中重复的元素-1

在这里插入图片描述

代码

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param head ListNode类 
 * @return ListNode类
 */
struct ListNode* deleteDuplicates(struct ListNode* head ) {
    // write code here
    struct ListNode* cur = head;
    if(head == NULL){
        return NULL;
    }
    while(cur->next != NULL){//因为后面需要操作cue->next,因此需要保证cur->next不为空
        //相等,删除
        if(cur->val == cur->next->val){
            struct ListNode* temp = cur->next;
            cur->next = cur->next->next;
            free(temp);
        }
        else{
            cur = cur->next;
        }
    }
    return head;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

删除链表中倒数第k个节点(快慢指针)

在这里插入图片描述

代码(不用虚拟头节点)

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param head ListNode类 
 * @param n int整型 
 * @return ListNode类
 */
struct ListNode* removeNthFromEnd(struct ListNode* head, int n ) {
    // write code here
    struct ListNode* fast = head;
    struct ListNode* slow = head;
    //第0种情况,链表长度<n
    if(head == NULL)
    {
        return NULL;
    }
    for(int i = 0; i<n; i++){
        if(fast == NULL){
            return NULL;
        }
        fast = fast->next;
    }
    //第一种情况,刚刚好n等于链表长度
    if(fast == NULL)
    {
        return head->next;
    }
    //然后移动先fast,因为需要删除slow->next节点
    fast = fast->next;
    //第二种情况,链表长度>n
    while(fast != NULL)
    {
        fast = fast->next;
        slow = slow->next;
    }
    struct ListNode* temp = slow->next;
    slow->next = slow->next->next;
    free(temp);
    return head;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

代码(虚拟头节点)

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param head ListNode类 
 * @param n int整型 
 * @return ListNode类
 */
struct ListNode* removeNthFromEnd(struct ListNode* head, int n ) {
    // write code here
    struct ListNode* dummyhead = (struct ListNode*)malloc(sizeof(struct ListNode));
    dummyhead->next = head;
    dummyhead->val = 0;

    struct ListNode* fast = head;
    struct ListNode* slow = dummyhead;

    if(head == NULL)
    {
        return NULL;
    }
    for(int i = 0; i<n; i++){
        if(fast == NULL){
            return NULL;
        }
        fast = fast->next;
    }
    while(fast != NULL)
    {
        fast = fast->next;
        slow = slow->next;
    }
    struct ListNode* temp = slow->next;
    slow->next = slow->next->next;
    head = dummyhead->next;
    free(temp);
    free(dummyhead);
    return head;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

链表中倒数最后k个节点

在这里插入图片描述

思路

快慢指针,先用快指针走k步,如果k的大小没有超过链表的大小,则快慢指针同时移动,这样两者时钟保持k距离,当快指针指向空时,返回慢指针即为倒数的k个节点。

代码

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param pHead ListNode类 
 * @param k int整型 
 * @return ListNode类
 */
struct ListNode* FindKthToTail(struct ListNode* pHead, int k ) {
    // write code here
    typedef struct ListNode listnode;
    listnode* fast = pHead;
    listnode* slow = pHead;
    if(pHead == NULL)
    {
        return NULL;
    }
    for(int i = 0; i<k; i++)
    {
        if(fast == NULL)
        {
            return NULL;
        }
        fast = fast->next;
    }
    while(fast!=NULL)
    {
        slow = slow->next;
        fast=fast->next;
    }
    return slow;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

合并两个排序的链表

方法一:迭代版本求解
初始化:定义cur指向新链表的头结点
操作:

如果l1指向的结点值小于等于l2指向的结点值,则将l1指向的结点值链接到cur的next指针,然后l1指向下一个结点值
否则,让l2指向下一个结点值
循环步骤1,2,直到l1或者l2为nullptr
将l1或者l2剩下的部分链接到cur的后面
技巧
一般创建单链表,都会设一个虚拟头结点,也叫哨兵,因为这样每一个结点都有一个前驱结点。

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param pHead1 ListNode类 
 * @param pHead2 ListNode类 
 * @return ListNode类
 */
struct ListNode* Merge(struct ListNode* pHead1, struct ListNode* pHead2 ) {
    // write code here
    typedef struct ListNode listnode;
    listnode *dummyhead = (listnode*)malloc(sizeof(listnode));//dummyhead用来标记头节点的前一个节点
    listnode *cur = dummyhead;  //用来遍历链表
    while(pHead1 && pHead2){
        if(pHead1->val <= pHead2->val){
            //移动pHead1前先保存pHead1
            cur->next = pHead1;
            //改变pHead1,以便下次继续比较
            pHead1 = pHead1->next;
        }else{
            //移动pHead1前先保存pHead1
            cur->next = pHead2;
            //改变pHead2,以便下次继续比较
            pHead2 = pHead2->next;
        }
        cur = cur->next;
    }
    //将剩余的添加上,如果pHead1为空,则添加pHead2,否则添加pHead1
    cur->next = pHead1?pHead1:pHead2;
    return dummyhead->next;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

删除链表节点

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
返回删除后的链表的头节点。
示例 1:
输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
示例 2:
输入: head = [4,5,1,9], val = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */


struct ListNode* deleteNode(struct ListNode* head, int val){
    struct ListNode* dummy_head = (struct ListNode*)malloc(sizeof(struct ListNode));//创建虚拟头节点
    dummy_head->next = head;  //虚拟头节点的下一个节点就是头节点,有了虚拟头节点,删除操作一致
    struct ListNode* cur = dummy_head;  //如果需要删除头节点,那么就需要操作cur->next,因此cur指向虚拟头节点
    while(cur->next != NULL){  //cur->next不为空
        if(cur->next->val == val){
            struct ListNode* temp = cur->next;//创建临时节点来保存需要删除的节点
            cur->next = cur->next->next;//改变指向
            free(temp);//删除节点
        }else{
            cur = cur->next;//不相等,则往下遍历链表
        }
    }
    head = dummy_head->next;//头节点是虚拟头结点的下一个节点
    free(dummy_head); //释放虚拟头节点
    return head;  //返回节点
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

翻转链表

这里是引用

/**
 * struct ListNode {
 *	int val;
 *	struct ListNode *next;
 * };
 */
/**
 * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
 *
 * 
 * @param head ListNode类 
 * @return ListNode类
 */
struct ListNode* ReverseList(struct ListNode* head ) {
    // write code here
    struct ListNode* temp;
    struct ListNode* cur = head;
    struct ListNode* pre = NULL;
    while(cur != NULL)
    {
        temp = cur->next;  //temp保存下一个值,因为需要断开
        cur->next = pre;  //重新指向
        pre = cur;  //先移动pre指针
        cur = temp;  //再移动cur指针
    }
    return pre;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

怎么判断链表有没有环(快慢指针)

/* 判断链表是否有环 */
/*
给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。
*/

/*快慢指针实现*/
#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>


struct ListNode{
  int val;
  struct ListNode *next;
};

bool is_list_circle(struct ListNode *head){
  //快慢指针指向头节点
  struct ListNode *first = head;
  struct ListNode *slow = head;

  while(first != NULL && first->next != NULL)
  {
    first = first->next->next;
    slow = slow->next;
    if(first == slow)
    {
      return 1;
    }
  }
  return 0;
}

int main() {
    // 创建一个包含环的链表
    struct ListNode *node1 = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *node2 = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *node3 = (struct ListNode *)malloc(sizeof(struct ListNode));
    struct ListNode *node4 = (struct ListNode *)malloc(sizeof(struct ListNode));

    node1->val = 1;
    node2->val = 2;
    node3->val = 3;
    node4->val = 4;

    node1->next = node2;
    node2->next = node3;
    node3->next = node4;
    node4->next = node2; // 将最后一个节点指向第二个节点,形成环

    // 验证是否有环
    if (is_list_circle(node1)) {
        printf("链表中存在环\n");
    } else {
        printf("链表中不存在环\n");
    }

    // 释放动态分配的内存
    free(node1);
    free(node2);
    free(node3);
    free(node4);

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

链表是否有环,有环则找出环入口

据公式推导,快慢指针的相遇点到环形入口节点的距离等于头节点到环形列表的距离
在这里插入图片描述

#include<stdio.h>
#include<stdbool.h>
#include<stdlib.h>

struct Node{
  int val;
  struct Node* next;
}Node;

struct Node* if_huan(struct Node* head)
{
  struct Node* slow = head;
  struct Node* fast = head;
  while(fast != NULL && fast->next != NULL)
  {
    // 这里判断两个指针是否相等,所以移位操作放在前面
    fast = fast->next->next;
    slow = slow->next;
    if(fast == slow){
      struct Node* index1 = fast;
      struct Node* index2 = head;
      while(index1 != index2) 相交,开始找环形入口:分别从头部和从交点出发,找到相遇的点就是环形入口
      {
        index1 = index1->next;
        index2 = index2->next;
      }
      return index1;
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

手写单词倒序 who are you -> you are who

// 翻转字符串中指定范围的字符
void reverse(char* s, int start, int end) {
    for (int i = start, j = end; i < j; i++, j--) {
        int tmp = s[i];
        s[i] = s[j];
        s[j] = tmp;
    }
}

// 删除字符串两端和中间多余的空格
void removeExtraSpace(char* s) {
    int start = 0; // 指向字符串开头的指针
    int end = strlen(s) - 1; // 指向字符串结尾的指针
    while (s[start] == ' ') start++; // 移动指针 start,直到找到第一个非空格字符
    while (s[end] == ' ') end--; // 移动指针 end,直到找到第一个非空格字符
    int slow = 0; // 指向新字符串的下一个写入位置的指针
    for (int i = start; i <= end; i++) { // 遍历整个字符串
        if (s[i] == ' ' && s[i+1] == ' ')  { // 如果当前字符是空格,并且下一个字符也是空格,则跳过
            continue;
        }
        s[slow] = s[i]; // 否则,将当前字符复制到新字符串的 slow 位置
        slow++; // 将 slow 指针向后移动
    }
    s[slow] = '\0'; // 在新字符串的末尾添加一个空字符
}

// 翻转字符串中的单词
char * reverseWords(char * s){
    removeExtraSpace(s); // 先删除字符串两端和中间的多余空格
    reverse(s, 0, strlen(s) - 1); // 翻转整个字符串
    int slow = 0; // 指向每个单词的开头位置的指针
    for (int i = 0; i <= strlen(s); i++) { // 遍历整个字符串
        if (s[i] ==' ' || s[i] == '\0') { // 如果当前字符是空格或空字符,说明一个单词结束了
            reverse(s, slow, i-1); // 翻转单词
            slow = i + 1; // 将 slow 指针指向下一个单词的开头位置
        }
    }
    return s; // 返回处理后的字符串
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

手写一个判断字符串是否对称的函数。(左右指针)

/*
要判断一个字符串是否对称,可以使用双指针的方法。具体步骤如下:

使用两个指针,一个指向字符串的开头,另一个指向字符串的末尾。

分别向中间移动,比较两个指针指向的字符是否相等。

如果所有的字符都相等,那么字符串就是对称的。
*/
#include<stdio.h>
#include<string.h>
#include<stdbool.h>

bool isstring_Match(char* str)
{
  int left = 0;
  int right = strlen(str)-1;
  while(left < right)
  {
    if(str[left] != str[right])
    {
      return false;//表示字符串不对称
    }
    left++;
    right--;
  }
  return true;  //表示字符串对称
}

int main()
{
  char a[] = "aabbaa";
  char b[] = "abcdef";

  if(isstring_Match(a) == true)
  {
    printf("字符串%s对称\n",a);
  }
  else{
    printf("字符串%s不对称\n",a);
  }

  if(isstring_Match(b) == true)
  {
    printf("字符串%s对称\n",b);
  }
  else{
    printf("字符串%s不对称\n",b);
  }
  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

移除元素

/*
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1: 给定 nums = [3,2,2,3], val = 3, 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。 你不需要考虑数组中超出新长度后面的元素。

示例 2: 给定 nums = [0,1,2,2,3,0,4,2], val = 2, 函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。

你不需要考虑数组中超出新长度后面的元素。
*/
#include<stdio.h>
int removeElement(int arr[],int size, int val)
{
  int newSize = size;
  for(int i = 0; i<newSize; i++)
  {
    if(arr[i] == val)//发现需要移出的元素,整体元素向前移动
    {
      for(int j = i+1; j<size; j++)
      {
        arr[j-1] = arr[j];  //往前移动
      }
      i--;
      newSize--;
    }
  }
  return newSize;
}

int main()
{
  int arr[] = {1,2,2,3,4,3,5};
  int val = 3;
  int size = sizeof(arr) / sizeof(int);
  int newsize = removeElement(arr,size,val);
  printf("大小%d\n",newsize);
  printf("移除后的数组\n");
  for(int i = 0; i<newsize; i++)
  {
    printf("%d ",arr[i]);
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

输入一个整形,返回一个字符串距离2001.1.1号多少天

写了一个strcpy

/*
  strcpy 函数用于将一个字符串复制到另一个字符串中,直到遇到空字符 '\0' 为止。
*/
#include<stdio.h>

void my_strcpy(char* des, const char * src)
{
  while(*src != '\0')
  {
    *des = *src;
    des++;
    src++;
  }
  *des = '\0';
}

int main()
{
  char src[] = "asdfdsgadfg";
  char des[20];
  my_strcpy(des,src);

  printf("复制后的字符串des为%s",des);
  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

输入n个字符串 将其中的字符串排序(排序规则 字符串中字符大小的和)

括号匹配

在这里插入图片描述

C++

方法一:借助辅助栈——左括号入栈
核心思想:
每次遇到’(‘,’{‘,’[‘这三种字符的时候,将字符入栈stk;而每次遇到’)‘,’}‘,’]‘这三种字符的时候则让对应的匹配字符出栈。具体规则如下:
1)引入辅助栈stk,遍历字符串,每次遇到’(‘,’{‘,’[‘字符的时候将字符入栈stk
2)当遇到’)‘,’}‘,’]'字符的时候,则检查栈是否空,且顶元素是否为匹配元素(如{和}匹配等),如果栈空或者栈顶元素不为匹配元素则括号序列不合法
3)当栈非空,且栈顶元素为匹配元素,则栈顶元素出栈。
4)循环匹配字符串,直到每次字符处理完
5)检查栈stk是否为空,栈为空则序列合法,否则不合法(当括号以正确顺序关闭时则最后的栈为空)

class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param s string字符串 
     * @return bool布尔型
     */
    bool isValid(string s) {
        // write code here
        stack<char>st;
        //{  [   (  入栈
        //}  )   ]  出栈  判断栈是否为空,或,判断是否相等
        for(int i = 0; i<s.size(); i++){
            switch(s[i])
            {
                case '(':
                case '[':
                case '{':
                    st.push(s[i]);    //当前字符为'(','{','['时,元素入栈
                    break;
                case ']':
                    if(st.empty() || st.top() != '['){   //栈空或者括号栈顶字符与当前字符不匹配,则序列为不合法序列
                        return false;
                    }
                    st.pop();                 //匹配的栈顶元素出栈
                    break;
                case '}':
                    if(st.empty() || st.top() != '{'){
                        return false;
                    }
                    st.pop();
                    break;
                case ')':
                    if(st.empty() || st.top() != '('){
                        return false;
                    }
                    st.pop();
                    break;
            }
        }
        return st.empty()?true:false;  //当括号以正确顺序关闭时则最后的栈为空
    }
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

C语言

/*有效的括号
https://leetcode.cn/problems/valid-parentheses/solutions/373578/you-xiao-de-gua-hao-by-leetcode-solution/
测试案例:
1、字符串为空的情况
2、括号匹配的情况
3、括号不匹配的情况

方法一:栈
判断括号的有效性可以使用「栈」这一数据结构来解决。

我们遍历给定的字符串 sss。当我们遇到一个左括号时,我们会期望在后续的遍历中,有一个相同类型的右括号将其闭合。由于后遇到的左括号要先闭合,因此我们可以将这个左括号放入栈顶。

当我们遇到一个右括号时,我们需要将一个相同类型的左括号闭合。此时,我们可以取出栈顶的左括号并判断它们是否是相同类型的括号。如果不是相同的类型,或者栈中并没有左括号,那么字符串 sss 无效,返回 False\text{False}False。为了快速判断括号的类型,我们可以使用哈希表存储每一种括号。哈希表的键为右括号,值为相同类型的左括号。

在遍历结束后,如果栈中没有左括号,说明我们将字符串 sss 中的所有左括号闭合,返回 True\text{True}True,否则返回 False\text{False}False。

注意到有效字符串的长度一定为偶数,因此如果字符串的长度为奇数,我们可以直接返回 False\text{False}False,省去后续的遍历判断过程。

作者:力扣官方题解
链接:https://leetcode.cn/problems/valid-parentheses/solutions/373578/you-xiao-de-gua-hao-by-leetcode-solution/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
*/

#include<stdio.h>
#include<stdbool.h>
#include<string.h>

char pairs(char a)
{
  //三种括号的情况
  if(a == '}')return '{';
  if(a == ')')return '(';
  if(a == ']')return '[';
  return 0;
}

bool isValid(char *s)
{
  int n = strlen(s);
  if(n % 2 == 1)
  {
    return false;
  }
  int stk[100], top = 0;
  for (int i = 0; i < n; i++) {
      char ch = pairs(s[i]);  
      if (ch) {//如果当前字符是右括号,调用pairs函数找到与之匹配的左括号进行匹配
          if (top == 0 || stk[top - 1] != ch) {//如果栈为空(表示右括号多了),或者栈顶元素与匹配的左括号不相等,返回 false。
              return false;
          }
          top--;
      } else {//如果当前字符是左括号,将其入栈。
          stk[top++] = s[i];
      }
  }
  //遍历结束后,如果栈为空,说明所有括号都匹配成功,返回 true;否则(左括号多了)返回 false。
  return top == 0;
}

int main() {
    char str1[] = "([]{()})";
    char str2[] = "([)]";

    if (isValid(str1)) {
        printf("%s 括号匹配成功\n", str1);
    } else {
        printf("%s 括号匹配失败\n", str1);
    }

    if (isValid(str2)) {
        printf("%s 括号匹配成功\n", str2);
    } else {
        printf("%s 括号匹配失败\n", str2);
    }

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78

四数之和

指针数组

堆/栈/队列

用两个栈模拟一个队列。(考察点:定义栈,栈的压入与弹出,栈的判空)

在这里插入图片描述

使用栈实现队列的下列操作:

push(x) – 将一个元素放入队列的尾部。
pop() – 从队列首部移除元素。
peek() – 返回队列首部的元素。
empty() – 返回队列是否为空。

C++实现

###注意
只有当stack2为空的时候,才从stack1里导入数据,否则会出现错误。

class Solution
{
public:
    void push(int node) {
        stack1.push(node);
    }

    int pop() {
        //只有当stack2为空的时候,再从stack1里导入数据
        if(stack2.empty()){
            //从stack1导入数据直到stack1为空
            //然后,stack2装满了数据,后面弹出即可
            while(!stack1.empty()){
                stack2.push(stack1.top());
                stack1.pop();
            }
        }
        //保存需要弹出的数据
        int result = stack2.top();
        //弹出数据
        stack2.pop();
        //返回数据值
        return result;
    }

private:
    stack<int> stack1;
    stack<int> stack2;
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

找出满足1 1/2 1/3 …… 1/n<=m的n的最大值。(考察点:变量的数据类型,float类型变量如何判等)

/*指针数组是一个数组,每个数组存放了一个指针变量*/
#include <stdio.h>
#include <string.h>

/*方法一 */
// int main()
// {
//   int a = 1;
//   int b = 2;
//   int c = 3;
//   int d = 4;
//   int e = 5;

//   int *p[5] = {&a,&b,&c,&d,&e};

//   for(int i = 0; i<5; i++){
//     printf("%d ",*p[i]);
//   }

//   return 0;
// }

/*方法二 数组存放字符指针*/
int main()
{
  char *p[5] =  {
    "aaa",
    "bbb",
    "ccc",
    "ddd",
    "ccc",
  };

  for(int i = 0; i<5; i++)
  {
    printf("%s\n",p[i]);//printf语句中p[i]已经是一个指向字符数组的指针,不需要再进行解引用
  }

  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

题1

char *a[] = {"BEIJING", "SHENZHEN", "SHANGHAI", "GUANGZHOU"};
char **pa[] = {a+3, a+2, a+1, a};
char ***ppa = pa;
int main(void) {
    printf("%s, ", **++ppa);
    printf("%s, ", *--*++ppa+3);
    printf("%s, ", *ppa[-2]+3);
    printf("%s", ppa[-1][-1]+1);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在这里插入图片描述
在这里插入图片描述

解释

/*
  对于指针ppa
        指针类型:char ***
        指针指向:pa[0]
        指针指向类型:char **
  //指针的指向决定其偏移起始位置,指针指向的类型决定其每次移动偏移量大小 
*/
printf("%s, ", **++ppa);
/*
    运算符优先级:++ 与 * 运算符同优先级,结合性都是自右向左
    ++ppa
        ppa自增,指向改变,向后移动一位,偏移量为char **,指向pa[1]
    *++ppa
        取出内容,pa[1],pa[1]又指向a[2]
    **++ppa
        取出内容,a[2],a[2]又指向字符串"SHANGHAI",printf输出 SHANGHAI
*/
printf("%s, ", *--*++ppa+3);
/*
    运算符优先级:++/-- 优先级大于 + ,且 + 的结合性是从左往右
    ++ppa
        上一次操作已改变ppa的指向为pa[1],再次移动,指向pa[2]
    *++ppa
        取出内容,pa[2],pa[2]又指向a[1]
    --*++ppa
        //此时,【*++ppa】是一个char **指针,指向a[1],
//则其偏移的起始位置为a[1],偏移量为char *
        --使其自减,指针向前移动,从a[1]向前移动一个char*的大小,指向a[0]
    *--*++ppa
        取出内容,a[0],a[0]又指向字符串"BEIJING"
    *--*++ppa+3
        //此时【*--*++ppa】是一个char *指针,指向一个字符串,则其偏移的起始位置
//为字符串的首字符,偏移量为char
        +3使指针向后移动3次,每次移动一个char,移动完成后指向字符'J' ,printf
输出,从字符'J'的位置往后输出打印到STDOUT,输出 JING
*/
 printf("%s, ", *ppa[-2]+3);
/*
    []作用符:【例子】
    int arr[]{ 0,1,2,3 };
    int *p=arr;
    ++p;
    cout << p[1] << " " << *(p + 1) << endl;
    cout << p[-1] << " " << *(p - 1) << endl;
    //p[1]等价于*(p + 1)   p[-1]等价于*(p - 1)
    ppa[-2]
        等价于*(ppa-2)
            ppa-2
                //经过前两次操作,ppa指向pa[2],移动后指向pa[0],【但ppa
//本身的指向并没有改变,还是指向pa[2]】
            *(ppa-2)
                取出内容,pa[0],pa[0]又指向a[3]
    *ppa[-2]
        取出内容,a[3],a[3]又指向字符串"GUANGZHOU"
    *ppa[-2]+3
        +3使指针移动到字符'N',printf输出 NGZHOU 
*/
printf("%s", ppa[-1][-1]+1);
/*
    //类似与ppa[-2]等价于*(ppa-2),ppa[-1][-1]等价于*(*(ppa-1)-1)
    ppa[-1]
        等价于*(ppa-1)
            //经过前三步操作,ppa指向pa[2],移动后,指向pa[1],
//取出内容pa[1],pa[1]又指向a[2]
    ppa[-1][-1]
        等价于*(*(ppa-1)-1)
            *(ppa-1)-1
                将上一步操作中的*(ppa-1)看做一个整体,是一个char **指针,
指向类型为char * 的a[2]
                则指针 *(ppa-1) 偏移的起始位置为a[2],向前移动一次,
偏移量为char *,移动后指向a[1]
            *(*(ppa-1)-1)
                取出内容,a[1],a[1]又指向字符串"SHENZHEN"
    ppa[-1][-1]+1
        +1指针其移动到字符'H',printf输出 HENZHEN
*/  

作者:务必
链接:https://www.nowcoder.com/exam/test/73812107/submission?examPageSource=Intelligent&pid=52854006&testCallback=https%3A%2F%2Fwww.nowcoder.com%2Fexam%2Fintelligent%3FquestionJobId%3D3%26tagId%3D21003&testclass=%E9%80%9A%E4%BF%A1%2F%E7%A1%AC%E4%BB%B6
来源:牛客网
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80

char_int转换

#include <stdio.h>

void main(void){
  unsigned char a = 256;
  int b;
  b = a;
 
  printf("%d,%d",a,b);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在这里插入图片描述
#include<stdio.h>

int main()
{
unsigned long i;//4个字节
unsigned char a = 0;//1个字节 8位
for (i = 38; i < 600; i++) {
a++;
printf(“a的值为%u\n”, a);
}

return 0;
  • 1

}

38~599->562
(562 % 256) = 50

因此答案是50

进制

https://zhuanlan.zhihu.com/p/459817484

在这里插入图片描述
在这里插入图片描述

全局变量

int a = 1, b = 2;
void funl(int a, int b) { printf("%d%d", a, b); }
 
void fun2() {
    a = 3;
    b = 4;
}
 
int main() {
    funl(5, 6);
    fun2();   //改变了全局变量的值
    printf("%d%d\n", a, b);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

字符、字符串和数组

  char a[] = "12345";
  char b[] = {'1','2','3','4','5'};
  • 1
  • 2

在这里插入图片描述

变量定义

在C语言中规定a[n]中n不能为变量类型。

int n = 10,a[n];  //错误
  • 1

函数指针的应用

在这里插入图片描述

#include<stdio.h>
#include<string.h>

char CalcStrLen(char* str) {
	printf("%s\n", str);
	return 1;
}

struct tagStrHeader {
	char* str;
	char(*Calc)(char* str);
};

void MyStructLen(struct tagStrHeader* cb) {
	cb->Calc(cb->str);
}

char a[] = "I love you";
struct tagStrHeader MyStr;

void main()
{
	MyStr.str = a;
	MyStr.Calc = CalcStrLen;  //函数指针指向CalcStreLen

	MyStr.Calc(MyStr.str);
	MyStructLen(&MyStr);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

计算地址

在这里插入图片描述

#include<stdio.h>

int main()
{
	unsigned char* p1;
	unsigned long* p2;
	p1 = (unsigned char*)0x804000;
	p2 = (unsigned long*)0x805000;
	printf("0x%x\n", p2 + 5);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在这里插入图片描述

结构体-内存对齐

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

类型转换(自动-强制)

https://blog.csdn.net/MereX/article/details/82626110
http://c.biancheng.net/view/1775.html

在这里插入图片描述

unsigned long Sum(long x, long y){
	return (x+y);
}

int main()
{
	unsigned long a = 10;
	long b = -15;
	long c = 0;
	c = Sum(((a+b>0)?a:b),b)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在这里插入图片描述

void foo(void)
{
    unsigned int a = 6;
    int b = -20;
    (a+b > 6)?puts(>6):puts(<6)}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

答案是>6,有符号和无符号在进行运算的时候会进行隐式类型转换,将有符号转换为无符号。
有一种说法就是,在进行不同类型的运算时,变量总是从小字节往大字节隐式类型转换。

大小端

https://blog.csdn.net/wwwlyj123321/article/details/100066463
https://blog.csdn.net/weixin_44788542/article/details/112245179
https://blog.csdn.net/xiao_yi_xiao/article/details/120747648
https://blog.csdn.net/weixin_45137562/article/details/115047607
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

联合体判断大小端

#include <stdio.h>
int Check_sys()
{
    union Un
    {
        char c;
        int i;
    }un;
    un.i = 1;
    //如果是大端机则是0x1000 0000 0000 0000
    //如果是小端机则是0x0000 0000 0000 0001
    return un.c;
}

int main()
{
    int ret=Check_sys();//由于联合体的特性,大端机ret=0(0000),小端机ret=1(0001)
    if (1 == ret)
    {
        printf("当前模式为小端存储\n");
    }
    else
    {
        printf("当前模式为大端存储\n");
    }
    return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

指针判断大小端

int Check_sys()
{
	int a = 1;
	char* p = (char*)&a;
	return *p;  //大端返回0,小端返回1
}
int main()
{
	int ret = Check_sys();
	if (1 == ret)
	{
		printf("当前模式为小端存储\n");
	}
	else
	{
		printf("当前模式为大端存储\n");
	}
	return 0;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

传输float数据,怎么设计,四个字节?

https://blog.csdn.net/weixin_43251807/article/details/132737022

#include <stdio.h>
#include <stdlib.h>
unsigned char byte[4];
int main()
{
	int i;
	unsigned char* Float_to_Byte(float f);    //声明float转4字节函数
	float Byte_to_Float(unsigned char *p);    //声明4字节转float函数

	float f = 3.1415,f1;
	unsigned char *p;
	p=Float_to_Byte(f);                //进行float到4字节转换,并将转换结构存放在指针p中
    printf("%x %x %x %x\r\n",*p,*(p+1),*(p+2),*(p+3));//打印转换出的字节结果
	f1= Byte_to_Float(p);            //将4字节转换成float
	printf("float data=%f\n",f1);    //打印转换结果
	system("pause");
	return 0;
}

/*将浮点数f转化为4个字节数据存放在byte[4]中*/
//这段代码的功能是将一个浮点数转化为四个字节的数据,并按照高字节到低字节的顺序存储在一个数组中。需要注意的是,强制类型转换可能会导致精度的丢失。
unsigned char* Float_to_Byte(float f)
{
	float float_data = 0;
	unsigned long longdata = 0;
	longdata = *(unsigned long*)&f;           //注意,会丢失精度
	byte[0] = (longdata & 0xFF000000) >> 24;
	//这行代码的目的是将浮点数 f 的二进制表示形式强制转换为一个无符号长整型,然后将结果存储在 longdata 中。这样的转换会导致精度的丢失,因为浮点数和整型的表示方式不同。
	byte[1] = (longdata & 0x00FF0000) >> 16;
	byte[2] = (longdata & 0x0000FF00) >> 8;
	byte[3] = (longdata & 0x000000FF);
	return byte;
}

/*将4个字节数据byte[4]转化为浮点数存放在*f中*/
/*在给定的代码中,将浮点数转换为无符号整型(unsigned long)的目的是为了提取浮点数的二进制表示形式,以便在字节级别上操作它。*/
float Byte_to_Float(unsigned char *p)
{
	float float_data=0;
	unsigned long longdata = 0;
	//这行代码将从指针 p 指向的四个字节数据合并成一个无符号长整型数 longdata。
	longdata = (*p<< 24) + (*(p+1) << 16) + (*(p + 2) << 8) + (*(p + 3) << 0);
	//这行代码的目的是将 longdata 的二进制表示形式强制转换为一个浮点数,然后将结果存储在 float_data 中。
	float_data = *(float*)&longdata;
	return float_data;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

float

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变。

#define BIT3 (0x1 << 3)
static int a;

//第一个设置a的bit3置1
void set_bit3(void){
	a |= BIT3;
}
//第二个设置a的bit3清零
void clear_bit3(void){
	a &= ~BIT3;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。

int *ptr;
ptr = (int*)0x67a9;
*ptr = 0xaa66;
  • 1
  • 2
  • 3

中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是,产生了一个新的关键字 __interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。

__interrupt double compute_area (double radius)
 
{
 
double area = PI * radius * radius;
 
printf("\nArea = %f", area);
 
return area;
 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

1)ISR 不能返回一个值。
2) ISR 不能传递参数。
3) 在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。

C语言整数自动转换原则

void foo(void)
 
{
 
unsigned int a = 6;
 
int b = -20;
 
(a+b > 6) ? puts("> 6") : puts("<= 6");
 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这 个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答案是输出是 “>6”。原因 是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。

内存管理

malloc

malloc函数用于申请动态内存空间,函数原型

#include <stdlib.h>
...
void *malloc(size_t size);
  • 1
  • 2
  • 3

malloc 函数向系统申请分配size个字节的内存空间,并返回一个指向这块空间的指针。如果调用成功,会返回一个指向申请的内存空间的指针,由于返回类型是void指针(void *),所以它可以被转换成任何类型的数据;如果函数调用失败,返回值是NULL。另外,如果size参数设置为0,返回值可能是NULL,但这并不意味着函数调用失败。
在这里插入图片描述

free

释放动态内存空间使用free函数,函数原型是

#include<stdlib.h>
...
void free(void *ptr);
  • 1
  • 2
  • 3

free函数释放ptr参数指向的内存空间。该内存空间必须是由malloc,calloc或realloc函数申请的;否则,该函数将导致未定义行为。

内存泄漏

总结:导致内存泄露的两种情况

1、隐式内存泄漏(即用完内存块没有及时使用free函数释放)

#include<stdio.h>
#include<stdlib.h>
int main(void){
	while(1){
	malloc(1024);
}
return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

如果代码恰好在一个循环中不断申请内存资源,那么程序就有可能出现崩溃。malloc应与free成对编写。

2、丢失内存块地址

#include<stdio.h>
#include<stdlib.h>
int main(void){
	int *ptr;
	int num  = 123;
	ptr = (int *)malloc(sizeof(int));
	if(ptr == null){
	printf("分配内存失败\n");
	exit(1);
}
	printf("请输入一个数字\n");
	scanf("%d",ptr);
	printf("你输入的整数是:%d\n",*ptr);
	ptr = &num;
	printf("你输入的整数是:%d\n",*ptr);
	free(ptr);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

在这里插入图片描述
由于malloc申请的内存块地址保存在ptr指针里,只有ptr知道这块可用内存的地址。所以当ptr被指向其他地方,这块内存就泄漏了。现在free信直响的变量,但这个变量是局部变量,不允许手动释放。

申请任意尺寸的内存空间

/* 动态内存管理 */
// #include<stdio.h>
// #include<stdlib.h>

// int main(void)
// {
//   int *ptr;
//   ptr = (int *)malloc(sizeof(int));

//   if(ptr == NULL)
//   {
//     printf("分配内存失败\n");
//     exit(1);
//   }
//   printf("请输入一个数字");
//   scanf("%d",ptr);
//   printf("你输入的整数是:%d\n",*ptr);
//   free(ptr);
//   return 0;
// }

// /*内存泄漏-1*/
// #include<stdio.h>
// #include<stdlib.h>
// int main(void){
// 	while(1){
// 	malloc(1024);
// }
// return 0;
// }

// /*内存泄漏2*/
// #include<stdio.h>
// #include<stdlib.h>
// int main(void){
// 	int *ptr;
// 	int num  = 123;
// 	ptr = (int *)malloc(sizeof(int));
// 	if(ptr == NULL){
// 	printf("分配内存失败\n");
// 	exit(1);
// }
// 	printf("请输入一个数字\n");
// 	scanf("%d",ptr);
// 	printf("你输入的整数是:%d\n",*ptr);
// 	ptr = &num;
// 	printf("你输入的整数是:%d\n",*ptr);
// 	free(ptr);
// 	return 0;
// }

/*申请任意尺寸内存空间*/
#include<stdio.h>
#include<stdlib.h>

int main(void){
	int *ptr = NULL;
	int num ,i;
	printf("请输入待录入整数的个数:");
  scanf("%d",&num);
  ptr = (int *)malloc(num*sizeof(int));
  for(i = 0; i<num; i++)
  {
    printf("请录入第%d个整数",i+1);
    scanf("%d",&ptr[i]);
  }
  printf("你录入的整数是:");
  for(i = 0; i<num; i++){

    printf("%d ",ptr[i]);

  }
  putchar('\n');
  free(ptr);
  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76

由于malloc并不会初始化申请的内存空间,所以需要自己进行初始化。可用循环来初始化,但建议用memset,需要包含string.h头文件。
在这里插入图片描述

calloc

calloc函数用于申请并初始化一系列内存空间,函数原型:

include<stdlib.h>
...
void *calloc(size_t nmemb, size_t size)
  • 1
  • 2
  • 3

calloc 函数与malloc函数的一个重要区别是:calloc函数在申请完内存后,自动初始化内存空间0,而malloc函数不进行初始化操作,里边数据是随机的。

//calloc()分配内存空间并初始化
int *ptr = (int* )calloc(8, sizeof(int));
//malloc()分配内存空间并初始化
int *ptr = (int *)malloc(8*sizeof(int));
memset(ptr,0,8*sizeof(int));
  • 1
  • 2
  • 3
  • 4
  • 5

realloc

对原来分配的内存空间进行扩展,没办法确保两次调用malloc函数分配的内存空间是线性连续的。
relloc函数用于重新分配内存空间,函数原型:

#include<stdlib.h>
...
void *relloc(void *ptr, size_t size);
  • 1
  • 2
  • 3

注意:

1、realloc函数将ptr指向的内存空间大小修改为size个字节
2、如果新分配的内存空间比原来的大,则旧内存块的数据不会发生改变;如果新分配的内存空间比原来的小,则可能导致数据丢失,请慎用
3、该函数将移动内存空间的数据并返回新的指针
4、如果ptr参数为NULL,那么调用该函数就相当于调用malloc(size)。
5、如果size参数为0,并且ptr参数不为NULL,那么调用该函数就相当于调用free(ptr)
6、除非ptr参数为NULL,否则,ptr的值必须由先前调用malloc、calloc或realloc函数返回

编写一个·程序,不断接收用户用户输入的整数,知道用户输入-1表示输入结束,将所有数据打印出来

在这里插入图片描述

栈和堆的发展方向

在这里插入图片描述
1、堆和其他区段一样,都是从低地址向高地址发展
2、栈则相反,是由高地址向低地址发展
在这里插入图片描述

宏定义

C语言三大预处理功能:宏定义、文件包含和条件编译

不带参宏定义

注意:
1、为了和普通的变量进行区分,宏的名字通常约定全部由大写字母组成
2、宏定义只是简单地进行替换,并且由于预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,所以编译器不会对宏定义进行语法检查。
3、宏定义不是说明或语句,末尾不必加分号
4、宏定义的作用范围是从定义的位置开始到整个程序结束。
5、可以用#undef命令终止宏定义的作用域
6、宏定义允许嵌套

带参宏定义

宏定义求出x和y两个参数中比较大的那一个

#define MAX(x,y) (((x)>(y))?(x):(y))

//注意,在宏定义的时候,宏的名字和参数之间不能有空格,否则会被当做无参数的宏定义,以下错误定义
#define MAX (x,y) (((x)>(y))?(x):(y))  
  • 1
  • 2
  • 3
  • 4

带参宏定义和函数的区别

1、宏定义过程中,并不需要为形参指定类型,因为宏定义只是进行机械替换,并不需要为参数分配内存空间
2、在函数中新参和实参是两个不同的变量,都有自己的内存空间和作用域,嗲用是要把实参的值传递给形参

内联函数

链接1
https://edu.csdn.net/skill/c/c-d21d6de040954559abee2dbad03ac4d1?typeId=18356&ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169528815516800182112880%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=169528815516800182112880&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~hot_rank-6-50877383-null-null.142v94insert_down28v1&utm_term=%E5%86%85%E8%81%94%E5%87%BD%E6%95%B0&spm=1018.2226.3001.4187
链接2
https://blog.csdn.net/qq_33757398/article/details/81390151?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169528815516800182112880%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=169528815516800182112880&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~hot_rank-1-81390151-null-null.142v94insert_down28v1&utm_term=%E5%86%85%E8%81%94%E5%87%BD%E6%95%B0&spm=1018.2226.3001.4187

空间换时间
1.什么是内联函数
用关键字inline修饰的函数就是内联函数。关键字在函数声明和定义的时候都要加上,不写系统还是会当成常规函数

2.内联函数与一般函数的区别
1)内联含函数比一般函数在前面多一个inline修饰符

2)内联函数是直接复制“镶嵌”到主函数中去的,就是将内联函数的代码直接放在内联函数的位置上,这与一般函数不同,主函数在调用一般函数的时候,是指令跳转到被调用函数的入口地址,执行完被调用函数后,指令再跳转回主函数上继续执行后面的代码;而由于内联函数是将函数的代码直接放在了函数的位置上,所以没有指令跳转,指令按顺序执行

3)一般函数的代码段只有一份,放在内存中的某个位置上,当程序调用它是,指令就跳转过来;当下一次程序调用它是,指令又跳转过来;而内联函数是程序中调用几次内联函数,内联函数的代码就会复制几份放在对应的位置上

4)内联函数一般在头文件中定义,而一般函数在头文件中声明,在cpp中定义

3.利与弊
利:避免了指令的来回跳转,加快程序执行速度

弊:代码被多次复制,增加了代码量,占用更多的内存空间

4.什么时候使用内联函数
1)函数本身内容比较少,代码比较短,函数功能相对简单

2)函数被调用得频繁,不如循环中的函数

5.什么时候不能使用内联函数
1)函数代码量多,功能复杂,体积庞大。对于这种函数,就算加上inline修饰符,系统也不一定会相应,可能还是会当成一般函数处理

2)递归函数不能使用内联函数

6.内联函数比宏函数更强大
在这里插入图片描述
为什么通过宏执行的结果是11呢,宏比较机械和简单,只是将传入的参数直接放上去就执行,所以int a = SUM(2 + 3);就相当于int a = 2 + 3 * 2 +3;由于乘法优先级更高,所以得到a的值为11;而在内联函数中,传入的参数是5,所以得到25

为了得到正确的结果,我们应该将宏改变为:

#define SUM(x) ((x)*(x))
  • 1

中断_interrupt关键字

中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展-让标准C支持中断。下面的代码就使用了_interrupt关键字去定义一个中断服务子程序(SR),请评论一下这段代码。

__interrupt double computer_area(double radius)
{
    double area = PI * radius * radius;
    printf("Area = %f", area);
    return area;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

这里ISR不能传入参数和返回值,做到短而有效率。
在许多的处理器/编译器中,浮点一般都是不可重入的。
有些处理器/编译器就是不允许在ISR中做浮点运算。
此外,ISR应该是短而有效率的,在ISR中做浮点运算和输出打印是不明智的。printf()经常有重入和性能上的问题

指针数组与数组指针

在这里插入图片描述

指针数组

在这里插入图片描述
在这里插入图片描述
指针数组通常用来保存一组字符串
在这里插入图片描述

数组指针

数组指针的本质就是指针,只不过它碰巧指向了数组
在这里插入图片描述
在这里插入图片描述
有了数组指针后,我们除了使用数组下标来遍历数组元素外,还可以使用数组来完成一样的事情。不同的是数组名是常量,它的值不能改变,而数组指针是变量,它更加灵活。
在这里插入图片描述

sizeof是操作符还是函数

sizeof是操作符。用于在编译时计算类型或表达式的大小。

串口通信的自定义协议,如何判断数据完整

在这里插入图片描述

malloc与new的区别

https://zhuanlan.zhihu.com/p/338489910
在这里插入图片描述

SPI和I2C寻址方式的区别

在这里插入图片描述

中断函数里为什么不能调用printf

https://zhuanlan.zhihu.com/p/389966062
https://blog.csdn.net/weixin_34266504/article/details/85901208?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.control
https://blog.csdn.net/networkhunter/article/details/83959254?utm_medium=distribute.pc_relevant.none-task-blog-title-3&spm=1001.2101.3001.4242

首先中断里肯定不适合调用printf,那么为什么呢?就比如上面的串口实现方式,就以9600,1个起始位,1个停止位,8个数据位的常见方式为例:
在这里插入图片描述
你看,传输一个字节要1个毫秒,如果打印好几个字节就是好几个毫秒了,所以答案几乎就已经很清楚了,在中断函数里打印,会增加中断函数执行的时间。**中断需要快进快出!**比如是一个串口逐字节接收中断函数,外部的报文逐字节输入,而中断函数先打印一点日志,好几个毫秒就过去了。如果UART外设是一个单字节的接收寄存器,那完了,报文指定被冲掉了。有的UART可能有多字节FIFO,但是即便是这样,也有很大的概率会被冲掉。

这是一个中断里不能调用printf的主要原因,执行费时!

另外如果编译环境配置printf不一样,这个内部实现也可能需要很多的存储空间。这对单片机而言也是不合算的。来比较一下,把printf去掉:
增加了printf,code区增加了将近7K字节。

推挽输出和开漏输出的区别

https://zhuanlan.zhihu.com/p/41942876
https://www.bilibili.com/video/BV1Pr4y1n74J/?spm_id_from=333.337.search-card.all.click&vd_source=a302e304b0aa60652c390b422ff81ab8
https://www.bilibili.com/video/BV1D84y1c7GV/?spm_id_from=333.788.recommend_more_video.-1&vd_source=a302e304b0aa60652c390b422ff81ab8

在这里插入图片描述
开漏模式作用1、改变高电平的电压
当MOS关闭时,也就是GPIO处于高阻态,enable就被上拉电阻拉到了3.3V
在这里插入图片描述

单片机输出的是5V,但芯片的enable只支持3.3V输入。
当MOS管打开时,enable就被拉到了低电平,从而实现5V单片机对3.3v芯片的控制。
在这里插入图片描述
开漏模式作用2、可以支持几个GPIO同时控制一个输入。
如果用推挽,则会短路(上输出高电平,下输出低电平)
在这里插入图片描述
因此,需要将两个GPIO配置为开漏模式,再加入一个上拉电阻。
只要有任意一个GPIO输出低电平,enable就是低电平,如果都处于高阻态,那么enable就是高电平。
在这里插入图片描述
在这里插入图片描述

推挽输出高电平
P-MOS导通,N-MOS截止
在这里插入图片描述
2、

推挽输出低电平
N-MOS导通,P-MOS截止
在这里插入图片描述
开漏输出的线与特性
1、如果全部引脚输出高电平,io才输出高电平
2、任意一个引脚输出低电平,全部io输出低电平
在这里插入图片描述
开漏的原因
在这里插入图片描述

在这里插入图片描述
普通输出与复用输出的区别
普通输出,直接编程通过这根绿色线能够控制它的输出高低电平
在这里插入图片描述
复用输出
复用功能输出线连接的是单片机内部外设,比如PWM,USART,I2C,由于通讯频率高,每一秒钟电平变化上万次,如果使用推挽或者开漏输出的话,那么我们就需要通过给这个引脚编程,来控制它的输出,而如果我们使用它的复用功能之后,它的内部外设模块直接控制它的引脚输出,极大方便编程。
在这里插入图片描述

条件编译

在这里插入图片描述

PWM(Pulse Width Modulation脉冲宽度调制)

使用数字信号达到一个模拟信号的效果。
惯性环节
面积等效原理:输出=输入乘以占空比
在这里插入图片描述

EEPROM和Flash的理解,端口及其功能

https://blog.csdn.net/chenhuanqiangnihao/article/details/123742940?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169613405116800225590406%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=169613405116800225590406&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allsobaiduend~default-2-123742940-null-null.142v94insert_down28v1&utm_term=eeprom%E5%92%8Cflash%E8%AF%A6%E8%A7%A3&spm=1018.2226.3001.4187

I2C中为什么需要开漏输出和上拉电阻

IIC协议支持多个主设备与多个从设备在一条总线上, 如果不用开漏输出, 而用推挽输出, 会出现主设备之间短路的情况.
至于为什么需要上拉电阻, 那是因为IIC通信需要输出高电平的能力.
为了实现多个主设备抢占总线时的仲裁.IIC只有两根线(SCL和SDA), 怎么判断哪个主设备占用总线(当然是先来后到了).

假设主设备A需要启动IIC, 他需要在SCL高电平时, 将SDA由高电平转换为低电平作为启动信号. 主设备A在把SDA拉高后, 它需要再检查一下SDA的电平。

为什么? 因为线与. 如果主设备A拉高SDA时, 已经有其他主设备将SDA拉低了. 由于 1 & 0 = 0 那么主设备A在检查SDA电平时, 会发现不是高电平, 而是低电平. 说明其他主设备抢占总线的时间比它早, 主设备A只能放弃占用总线. 如果是高电平, 则可以占用。

这就是开漏输出在IIC通信中的另一个作用。

SDA是高电平, 说明主设备A可以占用总线, 然后主设备A将SDA拉低, 开始通信.SDA是低电平, 说明有人已经捷足先登了, 主设备A不能占用总线, 结束通信.

因此, 模拟IIC一定要将GPIO端口设置为开漏输出并加上上拉电阻.(硬件IIC会自动配置为开漏输出)。
在这里插入图片描述

开漏输出

1、防止短路

在一些情况下(比如总线), 多个GPIO口可能会连接在同一根线上, 存在某个GPIO输出高电平, 另一个GPIO输出低电平的情况. 如果使用推挽输出, 你会发现这个GPIO的VCC和另一个GPIO的GND接在了一起, 也就是短路了. 如果换成开漏输出呢? VCC和GND多了个电阻, 这样电路就是安全的.所以总线一般会使用开漏输出.
在这里插入图片描述

2、线与功能

“线与”,只要有任意一个或多个设备输出低电平,总线就处于低电平,只有所有的设备都输出高电平,总线才处于高电平。

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号