小白问题:关于Cpp代码规范性 .h与.cpp文件的关系与内容的存疑

    • 咸寒少
      楼主

      最近由于更换IDE和CLI,需要将程序全部重写,于是也就考虑将之前不规范的代码整理成规范内容.

      原IDE:VEXcode Pro V5

      现IDE:PROS Editor(均为VEX机器人专用IDE)

      存疑内容:.h文件一般用于声明常量和函数 而 cpp文件一般用于表达函数逻辑.

      那我在引用.h文件时,编译器发现了声明的函数,它如何去寻找函数的逻辑内容?

      是去寻找一个cpp文件吗?要让两者关联有什么格式要求吗?

      常见.h的格式是什么样的,全局定义吗?

      为了防止重复编译还有哪些办法?除了#pragma once

      标签: 

      #21000
  • 孙锡源
    管理员

    我个人已经很多年不写C++了,以下内容只保证总体上正确:

    编译器如何寻找h文件对应的cpp文件

    这个涉及到C++编译器的编译过程:

    1. 预处理
    2. 编译
    3. 链接

    在预处理阶段,所有使用include宏标记的内容都会被原封不动的复制到当前文件中。

    比如说以下代码:

    include "hello.h"
      
    int main()
    {
            hello();
    
            return 0;
    }

    经过预处理后会变成这样:

    int main();
    
    int main() { 
        hello(); 
        return 0; 
    }

    可以发现,其实根本不存在所谓的“h文件与cpp文件关联”,因为h文件根本就是等于不存在,在预处理过程中它就会被全部复制到引用它的cpp中。

    关于include宏的介绍见GCC官方文档:https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html

    在其后的编译过程中,每个cpp文件会被单独编译,并各自生成二进制文件。如果你没在每个cpp文件头部声明要引用的类、函数的定义,则这个阶段就会抛出函数、类未定义的错误。

    最后的链接过程可以理解成是将多个编译后的cpp文件关联成一个整体,对于在同一个命名空间和作用域中的函数、类、变量,其链接后就是全局公用的。在这个阶段如果函数的逻辑部分未发现的话g++编辑器会抛出错误。

    常见.h的格式是什么样的

    如果你的代码不包含对外提供的接口的话可以不写h文件,而是使用extern关键字声明该函数、变量、类在此cpp文件外部定义。

    当然,不怕麻烦的话给每个cpp文件写一个h文件也可以,或者也可以把多个cpp文件的定义塞到一个h文件里。

    如果有一些全局配置信息的话可以写一个类似config.h这种文件,其中只包含一些常量的定义,就好像WordPress的wp-config.php一样。

    防止重复编译

    在Linux内核中多使用以下宏来避免重复包含:

    #ifndef FOOP_H
    #define FOOP_H
    int foo(int a);
    #endif

    比如这个关于ECC加密算法的头文件:

    https://github.com/torvalds/linux/blob/master/include/crypto/ecc_curve.h

  • 咸寒少
    楼主

    抱歉我还是不太懂你的意思。

    我写.h文件的目的是让给机器人下载的主函数文件尽量简洁。

    由于机器人的特殊性,我的函数要被反复反复利用。

    所以.h文件即起到函数分类的作用 也起到简洁代码的作用。

    我要是不想把逻辑写在我的主函数文件里,也不应该写在.h里面,那我应该把函数逻辑写在哪里,引用这个.h文件时,编译器才能找到它的逻辑。

  • 咸寒少
    楼主

    Adding timestamp [OK]
    Linking hot project with ./bin/cold.package.elf and libc,libm,libpros,okapilib [ERRORS]
    /usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:25: multiple definition of `LED’; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:25: first defined here
    /usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:22: multiple definition of `IS’; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:22: first defined here
    /usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:19: multiple definition of `RB’; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:19: first defined here
    /usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:18: multiple definition of `RF’; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:18: first defined here
    /usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:16: multiple definition of `ML’; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:16: first defined here
    /usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:15: multiple definition of `BL’; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:15: first defined here
    /usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:14: multiple definition of `FL’; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:14: first defined here
    /usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:13: multiple definition of `LB’; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:13: first defined here
    /usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:12: multiple definition of `LF’; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:12: first defined here
    /usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:9: multiple definition of `Ctrl2′; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:9: first defined here
    /usr/local/Cellar/arm-gcc-bin@10/10-2020-q4-major_1/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld: bin/main.cpp.o:/Users/sukanu/L3313B/./include/config.h:8: multiple definition of `Ctrl1′; bin/ctrl_func.cpp.o:/Users/sukanu/L3313B/./include/config.h:8: first defined here
    collect2: error: ld returned 1 exit status
    make: *** [bin/hot.package.elf] Error 1
    ERROR – pros.cli.build:build_compile_commands – Failed to make project: Exit Code 2
    Error: Failed to build

     

    重复定义错误

    我重复包含了config.h文件

    做了

    #ifndef CONFIG_H
    #define CONFIG_H
    #pragma once

    的处理

    但是还是无法编译

  • 孙锡源
    管理员

    代码贴上来看看。

    记得用编辑器的插入代码功能贴,别直接复制粘贴

  • 咸寒少
    楼主

    config.h

    _Pragma("once")
    #pragma once
    #ifndef _CONFIG_H_
    #define _CONFIG_H_
    
    #include "main.h"
    
    //---Declare Devices---
    //Ctrl
    Controller Ctrl1(CONTROLLER_MASTER);
    Controller Ctrl2(CONTROLLER_PARTNER);
    
    //Motor fwd
    Motor LF(12,E_MOTOR_GEARSET_18,false);
    Motor LB(13,E_MOTOR_GEARSET_18,false);
    Motor FL(15,E_MOTOR_GEARSET_18,false);
    Motor BL(16,E_MOTOR_GEARSET_18,false);
    Motor ML(19,E_MOTOR_GEARSET_18,false);
    //Motor rev
    Motor RF(17,E_MOTOR_GEARSET_18,true);
    Motor RB(18,E_MOTOR_GEARSET_18,true);
    
    //Imu
    Imu IS (14);
    
    //3-Wire LED
    ADIDigitalOut LED ('A');
    
    #endif

    ctrl_func.h

    #pragma once
    #ifndef _CTRL_FUNC_H
    #define _CTRL_FUNC_H
    
    #include "main.h"
    
    void STOP_MOVE();
    void STOP_LIFT();
    void LOCK();
    void Ctrl_L(int ctrl_power);
    
    #endif
    

    ctrl_func.cpp

    #include "main.h"
    #include "config.h"
    
    long long shin_count = 0;
    void Ctrl_Shin(bool status) {
      shin_count++;
      if (status) {
        if (shin_count % 10 == 0) {
          LED.set_value(1);
        }
        if (shin_count % 10 == 5) {
          LED.set_value(0);
        }
      } else {
        LED.set_value(0);
      }
    }
    
    void STOP_MOVE() {
      LF.set_brake_mode(E_MOTOR_BRAKE_BRAKE);
      LB.set_brake_mode(E_MOTOR_BRAKE_BRAKE);
      RF.set_brake_mode(E_MOTOR_BRAKE_BRAKE);
      RB.set_brake_mode(E_MOTOR_BRAKE_BRAKE);
    }
    
    void STOP_LIFT() {
      FL.set_brake_mode(E_MOTOR_BRAKE_HOLD);
      ML.set_brake_mode(E_MOTOR_BRAKE_HOLD);
      BL.set_brake_mode(E_MOTOR_BRAKE_HOLD);
    }
    
    void LOCK() {
      STOP_MOVE();
      STOP_LIFT();
    }
    
    void Ctrl_L(int ctrl_power) {
      if (ctrl_power == 0) {
        LF.set_brake_mode(E_MOTOR_BRAKE_BRAKE);
        LB.set_brake_mode(E_MOTOR_BRAKE_BRAKE);
      } else {
        LF.move(ctrl_power);
        LB.move(ctrl_power);
      }
    }
    

    main.cpp

    #include "main.h"
    #include "config.h"
    #include "ctrl_func.h"
    
    /**
     * Runs initialization code. This occurs as soon as the program is started.
     *
     * All other competition modes are blocked by initialize; it is recommended
     * to keep execution time for this mode under a few seconds.
     */
    void initialize() {
           LED.set_value(1);
           LF.set_zero_position(0);
      LB.set_zero_position(0);
      RF.set_zero_position(0);
      RB.set_zero_position(0);
      FL.set_zero_position(0);
      BL.set_zero_position(0);
      ML.set_zero_position(0);
      IS.reset();
           LED.set_value(0);
    }
    
    /**
     * Runs while the robot is in the disabled state of Field Management System or
     * the VEX Competition Switch, following either autonomous or opcontrol. When
     * the robot is enabled, this task will exit.
     */
    void disabled() {
           LOCK();
    }
    
    /**
     * Runs after initialize(), and before autonomous when connected to the Field
     * Management System or the VEX Competition Switch. This is intended for
     * competition-specific initialization routines, such as an autonomous selector
     * on the LCD.
     *
     * This task will exit when the robot is enabled and autonomous or opcontrol
     * starts.
     */
    void competition_initialize() {}
    
    /**
     * Runs the user autonomous code. This function will be started in its own task
     * with the default priority and stack size whenever the robot is enabled via
     * the Field Management System or the VEX Competition Switch in the autonomous
     * mode. Alternatively, this function may be called in initialize or opcontrol
     * for non-competition testing purposes.
     *
     * If the robot is disabled or communications is lost, the autonomous task
     * will be stopped. Re-enabling the robot will restart the task, not re-start it
     * from where it left off.
     */
    void autonomous() {}
    
    /**
     * Runs the operator control code. This function will be started in its own task
     * with the default priority and stack size whenever the robot is enabled via
     * the Field Management System or the VEX Competition Switch in the operator
     * control mode.
     *
     * If no competition control is connected, this function will run immediately
     * following initialize().
     *
     * If the robot is disabled or communications is lost, the
     * operator control task will be stopped. Re-enabling the robot will restart the
     * task, not resume it from where it left off.
     */
    void opcontrol() {
           Controller master(E_CONTROLLER_MASTER);
           Motor left_mtr(1);
           Motor right_mtr(2);
    
           while (true) {
                  lcd::print(0, "%d %d %d", (lcd::read_buttons() & LCD_BTN_LEFT) >> 2,
                                   (lcd::read_buttons() & LCD_BTN_CENTER) >> 1,
                                   (lcd::read_buttons() & LCD_BTN_RIGHT) >> 0);
                  int left = master.get_analog(ANALOG_LEFT_Y);
                  int right = master.get_analog(ANALOG_RIGHT_Y);
    
                  left_mtr = left;
                  right_mtr = right;
                  delay(10);
           }
    }
    

    main.h

    /**
     * \file main.h
     *
     * Contains common definitions and header files used throughout your PROS
     * project.
     *
     * Copyright (c) 2017-2021, Purdue University ACM SIGBots.
     * All rights reserved.
     *
     * This Source Code Form is subject to the terms of the Mozilla Public
     * License, v. 2.0. If a copy of the MPL was not distributed with this
     * file, You can obtain one at http://mozilla.org/MPL/2.0/.
     */
    
    #ifndef _PROS_MAIN_H_
    #define _PROS_MAIN_H_
    
    /**
     * If defined, some commonly used enums will have preprocessor macros which give
     * a shorter, more convenient naming pattern. If this isn't desired, simply
     * comment the following line out.
     *
     * For instance, E_CONTROLLER_MASTER has a shorter name: CONTROLLER_MASTER.
     * E_CONTROLLER_MASTER is pedantically correct within the PROS styleguide, but
     * not convienent for most student programmers.
     */
    #define PROS_USE_SIMPLE_NAMES
    
    /**
     * If defined, C++ literals will be available for use. All literals are in the
     * pros::literals namespace.
     *
     * For instance, you can do 4_mtr = 50 to set motor 4's target velocity to 50
     */
    #define PROS_USE_LITERALS
    
    #include "api.h"
    #include <bits/stdc++.h>//Added by Sukanu
    
    /**
     * You should add more #includes here
     */
    //#include "okapi/api.hpp"
    //#include "pros/api_legacy.h"
    
    /**
     * If you find doing pros::Motor() to be tedious and you'd prefer just to do
     * Motor, you can use the namespace with the following commented out line.
     *
     * IMPORTANT: Only the okapi or pros namespace may be used, not both
     * concurrently! The okapi namespace will export all symbols inside the pros
     * namespace.
     */
    using namespace pros;
    using namespace std;//Added by Sukanu
    // using namespace pros::literals;
    // using namespace okapi;
    
    /**
     * Prototypes for the competition control tasks are redefined here to ensure
     * that they can be called from user code (i.e. calling autonomous from a
     * button press in opcontrol() for testing purposes).
     */
    #ifdef __cplusplus
    extern "C" {
    #endif
    void autonomous(void);
    void initialize(void);
    void disabled(void);
    void competition_initialize(void);
    void opcontrol(void);
    #ifdef __cplusplus
    }
    #endif
    
    #ifdef __cplusplus
    /**
     * You can add C++-only headers here
     */
    #include <iostream>
    #endif
    
    #endif  // _PROS_MAIN_H_
    

     

  • 孙锡源
    管理员

    先把代码改成最简版本。就只留下config.h引入相关的代码,然后config.h里面只留下一个常量,去掉其他所有注释和无关代码。目前这一大摊把我眼睛看瞎了,尤其是你这代码依赖特定环境,又不能在本地调试。

    另外,在其他.h里面引入main.h是什么操作?

    最后你的项目目录是/Users/sukanu/L3313B?

  • 正在查看 6 条回复
    • 哎呀,回复话题必需登录。

    加入 LitePress 论坛 ,参与知识分享与交流
    登录 注册 进行评论
    立即加入