如何区分c中函数的INPUT,OUTPUT和INPUT-OUTPUT参数?

时间:2016-02-15 18:03:03

标签: c++ c function pointers parameters

阅读"软件详细说明"一些软件模块的文档,我在每个函数的描述中看到:

  1. INPUT PARAMETER:.....
  2. OUTPUT PARAMETER:.....
  3. INPUT-OUTPUT PARAMETER:.....
  4. 例如,我们有以下内容:

    typedef struct
    {
        u16_t elementA;
        u16_t elementB;
        u8_t  elementC;
    
    } myStruct;
    
    
    void somefunction(myStruct *pToMyStruct)
    {
    
       pToMyStruct->elementA  = 1;
       pToMyStruct->elementB  = 5;
       pToMyStruct->elementC  = 7;
    }
    

    在软件描述文档中,描述如下:

    1. INPUT PARAMETER:none
    2. OUTPUT PARAMETER:none
    3. INPUT-OUTPUT PARAMETER:指向结构的指针(pToMyStruct)
    4. 我对c编程技术没有那么深刻,但为什么在这种情况下是" pToMyStruct" INPUT-OUTPUT参数?为什么它不仅仅是一个INPUT参数?作为一个没有经验的程序员但是理解代码,我怎样才能在函数中轻松识别这3种类型的参数?例如,INPUT参数只能在它自己的函数中修改,或者?

      非常感谢!

7 个答案:

答案 0 :(得分:3)

// INPUT-OUTPUT PARAMETER: pointer to structure (pToMyStruct)
void somefunction(myStruct *pToMyStruct) {
   pToMyStruct->elementA  = 1;
   ...
}

“为什么它不仅仅是一个INPUT参数?” - 从技术上讲,你是对的:它只是一个输入参数。函数的唯一输出是它的返回值及其对全局环境的影响(全局变量,printf()等)。

“软件详细说明”(作者)坚持“C-does-not-pass-by-reference”,因此想要创建一个新的“观点”。如下所示:让我们将输入指针参数调用为伪传递引用,即:输入,输出或IO。作者希望您记录该模型。

// the return value is an output
int foo()

// x is an input parameter
void foo(int x)

// what x points to is an input parameter, be it an `int` or array of `int` or NULL
void foo(const int *x)

// what x points to is an output parameter 
void foo(int *x) {
  // *x not read before being set

// what x points to is an I/O parameter
void foo(int *x) {
  // *x read before being set

因此myStruct *pToMyStruct是输出或IO参数。如果没有探测到函数的主体,那么将其归类为IO更安全,因为函数签名允许读取和写入*pToMyStruct。给定简单的示例函数,它是一个输出参数。

答案 1 :(得分:2)

这是因为正在传递一个指向结构的指针,并且它不是<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"/> <title>Google Maps</title> <script async defer src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAs4c8xnkxcZNRK6yQt-Y21N1L3mT1AFfE&callback=initMap"> </script> </head> <body> <div id="map" style="width: 1024px; height: 768px"></div> <noscript><b>JavaScript must be enabled in order for you to use Google Maps.</b> However, it seems JavaScript is either disabled or not supported by your browser. To view Google Maps, enable JavaScript by changing your browser options, and then try again. </noscript> <script type="text/javascript"> //<![CDATA[ // A function to create the marker and set up the event window function createMarker(point,name) { var marker = new google.maps.Marker({position: point, title: name}); google.maps.event.addListener(marker, "click", function(){ marker.openInfoWindowHtml(name);}); return marker; } function initMap() { var map = new google.maps.Map(document.getElementById('map'));//, { center: {lat: -34.397, lng: 150.644},zoom: 8}); var optionsCarte = { zoom: 8, center: new google.maps.LatLng(48.5, 2.9), mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById('map'), optionsCarte); var bounds = new google.maps.LatLngBounds(); // ========== Read paramaters that have been passed in ========== // If there are any parameters at the end of the URL, they will be in location.search // looking something like "?q=My+First+Point@59.591,17.82" // skip the first character, we are not interested in the "?" var query = location.search.substring(1); // split the rest at each "&" character to give a list of "argname=value" pairs var pairs = query.split("&"); for (var i=0; i<pairs.length; i++) { // break each pair at the first "=" to obtain the argname and value var pos = pairs[i].indexOf("="); var argname = pairs[i].substring(0,pos).toLowerCase(); var value = pairs[i].substring(pos+1); // process each possible argname - use unescape() if theres any chance of spaces if (argname == "q") { var text = unescape(value); var parts = text.split("@"); var latlng = parts[1].split(","); var point = new google.maps.LatLng(parseFloat(latlng[0]),parseFloat(latlng[1])); var title = parts[0]; var marker = createMarker(point,title); marker.setMap(map); bounds.extend(point); } } //map.setZoom(map.getBoundsZoomLevel(bounds)); map.fitBounds(bounds) map.setCenter(bounds.getCenter()); } </script> - 合格的。这是规则

  1. input:一个普通的参数,该函数不会修改。这可以是const指针。
  2. output:指向函数将在其修改的变量的指针 回报。在调用函数之前,该变量未初始化。
  3. 输入输出:以上两者的组合。传递已指向有效数据的指针,当函数返回时,数据将以某种方式更改。 const函数就是一个很好的例子。

答案 2 :(得分:2)

如说明书中所述,pToMyStruct是一个指针,这意味着该参数实际上是结构数据的内存地址。因此,传输的数据可以使用但也可以修改,这就是它是INPUT-OUTPUT参数的原因。

INPUT参数是一个无法在函数中修改的参数,就像普通变量一样。

一个简单的OUTPUT参数基本上是函数返回的值。

问题是函数通常需要有几个OUTPUT变量。要做到这一点,必须使用作为参数给出的指针。因此,除非您非常了解代码,否则无法推断参数中的指针是仅INPUT,OUTPUT还是INPUT-OUTPUT。了解它的唯一方法就是按照您的示例查看文档。

答案 3 :(得分:1)

输入 - 您只是将值/参数传递给函数。 output - 该函数将更新传递的变量的值(这可能只有C中的指针和C ++中的指针或引用) intput和output - 可以使用相同的变量来传递值,也可以传递给                      获取更新的值,变量类型与o / p相同。

在你的代码中,传递了一个结构变量,你在函数中得到一个更新的结构,你调用somefunction()。因此它是输入和输出变量。

答案 4 :(得分:1)

C中的参数始终按副本传递。当你使用指针时,它会执行一个指针的副本,它指向&#34;指向&#34;对同一个对象。然后,如果您修改&#34;指针副本的内容&#34;它会修改&#34;真实对象&#34;。

因此,它是一个输入/输出参数(因为可以修改它)。

如果你有了

void somefunction(myStruct theStruct)

那么结构本身就会被复制;那么它将是一个输入参数。

答案 5 :(得分:1)

与其他一些语言相反,C(以及C ++)并没有真正的关键字来表明,什么参数是什么,但作为经验法则:

  • 输入参数通常是指向const(或按值传递)
  • 的指针
  • 如果可能的话,你应该避免使用纯输出参数,而是使用返回值(这就是它们的用途)。
  • 因此,除非文档另有说明或函数名称明显,否则我假设指向非const参数的指针表示输入输出参数。

编辑:
我应该提到第二点(以及第三点)可能有点争议,因为有很多API使用纯输出参数 - 有些是有充分理由的(例如因为它们使用返回值来表示失败或成功),有些因为它们可以追溯到时代,当编译器中有很多笨重的时候,这实际上是一个重要的性能优化。

答案 6 :(得分:1)

  

但为什么在这种情况下“pToMyStruct”是一个INPUT-OUTPUT参数?   为什么它不仅仅是一个INPUT参数?作为一个没有经验的人,我怎么能   程序员但要了解代码,轻松识别这3种类型   函数中的参数?例如INPUT参数   在它自己的功能中修改,

我喜欢这个问题。也许只是在一些想法中回答太广泛,但这里有一些开始:

  • 构思1 - 函数/方法无法修改的形式参数只能输入到函数或方法中。

因此“const T formalParam”显然是一个输入......如果函数试图修改const T formalParam,则const命令编译器声明错误。

同样,“const T&amp; formalParam”是一个输入。有些人使用此参数样式来提供对大型数据项的访问,同时避免了复制的成本。

  • 构思2 - 形式参数是实际参数的副本,如传值,只能输入到函数或方法。

因此在“void foo(int t1);”中,int类型的形式参数t1是实际参数的副本。更改t1对实际int没有影响。此参数的行为就像是使用实际参数值的副本预先初始化的本地自动变量。

这也适用于传递类实例。所以“void foo(T t2)”,形式参数是一些实际参数的副本。同样,修改副本对原始文件没有影响,因此这只能是输入。

  • 构思3 - 作为现有实际参数的引用(或指针)的形式参数,如在引用中传递(也许您可能会说通过指针)可以是输出或输入/输出参数。

因此,在“void foo(int&amp; t1,char * label)”中,两个形式参数都可以是输出或输入/输出。而且您只能通过查看形式参数在方法代码中的使用方式来判断。

  • 想法4 - 当我第一次使用C ++时,我发现研究图书馆功能提供了丰富的信息。一些例子:
void* memcpy( void* dest, const void* src, size_t count );
// wrt memcpy: ^^output   ^^^^^input       ^input:pass-by-value
// Note direction:   to <---move <---from
// I think this right-to-left idea might be a trend in C (research?)
// return would seem useless, but is sometimes convenient value: copy of dest


int memcmp ( const void* lhs, const void* rhs, size_t count );
//           ^^^^^input       ^^^^^input       ^input:pass-by-value
// return is result of comparison negative: int, 0, positive int


int isalpha( int ch );
//           ^input:pass-by-value
// return is 0 (false) or non-zero (true)



//from std::string::find

size_type find( const basic_string& str, size_type pos = 0 ) const;
//              ^^^^^ input              ^input: pass-by-value
// return is npos or index where str found
  • 创意4 - 您的下一个工作可能会指定输入输出的序列,并产生称为“编码标准”的内容。

我已经回顾了一些Google的编码标准,不难发现。已经出版了关于这一主题的书籍。 (研究工作:搜索C ++编码标准,或者可能是C编码标准,并进行审查。)

  • 创意5 - 咨询他人

我更喜欢我的方法的返回值是“状态”,而不是方法或函数的结果。 (状态表示GOOD或FAIL)结果,如果我的代码输出了某些东西,那么它必须至少有1个输出参数,但方法通常会修改类实例的数据属性(而不是输出参数)。 我通常将它们安排为:

T  foo ( <input parameters> , <output parameters> );

T可以是任何具有简单快速评估的类。最近,我使用了std :: string,当string.size()为0时,表示函数期间没有错误。当size为正数时,该字符串包含用于报告的错误消息。