C Language
परिवर्तनशील तर्क
खोज…
परिचय
चर तर्क का उपयोग प्रिंटफ़ परिवार ( printf
, fprintf
, इत्यादि) और अन्य कार्यों में फ़ंक्शन द्वारा किया जाता है ताकि किसी फ़ंक्शन को हर बार विभिन्न प्रकार के तर्कों के साथ बुलाया जा सके, इसलिए नाम वैराग ।
चर तर्कों की सुविधा का उपयोग करके कार्यों को लागू करने के लिए, #include <stdarg.h>
उपयोग करें।
ऐसे कार्यों को कॉल करने के लिए जो एक परिवर्तनशील संख्या में तर्क देते हैं, यह सुनिश्चित करते हैं कि दायरे में अनुगामी दीर्घवृत्त के साथ एक पूर्ण प्रोटोटाइप है: void err_exit(const char *format, ...);
उदाहरण के लिए।
वाक्य - विन्यास
- शून्य va_start (va_list ap, last ); / * प्रारंभ चर तर्क प्रसंस्करण; अंतिम एलिप्सिस ("...") से पहले अंतिम फ़ंक्शन पैरामीटर है * /
- type va_arg (va_list ap, टाइप ); / * सूची में अगला परिवर्तनशील तर्क प्राप्त करें; सही प्रचारित प्रकार पास करना सुनिश्चित करें * /
- void va_end (va_list ap); / * अंतिम तर्क प्रसंस्करण * /
- शून्य va_copy (va_list dst, va_list src); / * C99 या बाद में: प्रतिलिपि तर्क सूची, अर्थात तर्क प्रसंस्करण में वर्तमान स्थिति, एक अन्य सूची में (उदाहरण के लिए कई शर्तों पर पास) * /
पैरामीटर
पैरामीटर | विवरण |
---|---|
va_list एपी | तर्क सूचक, वैरेडिक तर्कों की सूची में वर्तमान स्थिति |
अंतिम | अंतिम गैर-वैराडिक फ़ंक्शन तर्क का नाम, इसलिए संकलक को वैरेडिक तर्कों को संसाधित करने के लिए सही स्थान मिल जाता है; एक register चर, एक समारोह, या एक सरणी प्रकार के रूप में घोषित नहीं किया जा सकता है |
प्रकार | variadic तर्क के पदोन्नत प्रकार (उदाहरण के लिए पढ़ने के लिए int एक के लिए short int तर्क) |
va_list src | वर्तमान तर्क सूचक को कॉपी करने के लिए |
va_list dst | में भरने के लिए नई तर्क सूची |
टिप्पणियों
va_start
, va_arg
, va_end
, और va_copy
फ़ंक्शन वास्तव में मैक्रोज़ हैं।
हमेशा va_start
पहले कॉल करना सुनिश्चित करें, और केवल एक बार, और अंतिम, और केवल एक बार va_end
को कॉल करें, और फ़ंक्शन के प्रत्येक निकास बिंदु पर। ऐसा न करना आपके सिस्टम पर काम कर सकता है लेकिन निश्चित रूप से पोर्टेबल नहीं है और इस तरह बग को आमंत्रित करता है।
अपने फ़ंक्शन को सही ढंग से घोषित करने का ध्यान रखें, यानी एक प्रोटोटाइप के साथ, और पिछले गैर-वैरिएड तर्क पर प्रतिबंधों का ध्यान रखें ( register
नहीं, फ़ंक्शन या सरणी प्रकार नहीं)। एक फ़ंक्शन घोषित करना संभव नहीं है जो केवल वैरिएड तर्क लेता है, क्योंकि तर्क प्रसंस्करण शुरू करने में सक्षम होने के लिए कम से कम एक गैर-वैरेडिक तर्क की आवश्यकता होती है।
va_arg
कॉल करते va_arg
, आपको पदोन्नत तर्क प्रकार का अनुरोध करना चाहिए, जो है:
-
short
कोint
(औरunsigned short
को भीint
बढ़ावा दिया जाता है जब तक किsizeof(unsigned short) == sizeof(int)
, जिस स्थिति में इसेunsigned int
को बढ़ावा दिया जाता है)। -
float
कोdouble
करने के लिए बढ़ावा दिया जाता है। -
signed char
कोint
में पदोन्नत किया जाता है;unsigned char
को भीint
को बढ़ावा दिया जाता है जब तक किsizeof(unsigned char) == sizeof(int)
, जो शायद ही कभी मामला है। -
char
को आमतौर परint
बढ़ावा दिया जाता है। - जैसे C99 प्रकार
uint8_t
याint16_t
इसी तरह प्रचारित किया जाता है।
ऐतिहासिक (यानी K & R) वैरेडिक तर्क प्रसंस्करण <varargs.h>
में घोषित किया गया है, लेकिन इसका अप्रचलित होने के रूप में उपयोग नहीं किया जाना चाहिए। मानक परिवर्तनक तर्क प्रसंस्करण (यहाँ वर्णित और <stdarg.h>
में घोषित) को C89 में पेश किया गया था; va_copy
मैक्रो C99 में पेश किया गया था, लेकिन इससे पहले कई va_copy
द्वारा प्रदान किया गया था।
Va_list की लंबाई निर्धारित करने के लिए एक स्पष्ट गणना तर्क का उपयोग करना
किसी भी चर समारोह के साथ, फ़ंक्शन को पता होना चाहिए कि चर तर्कों की सूची की व्याख्या कैसे की जाए। printf()
या scanf()
फ़ंक्शन के साथ, प्रारूप स्ट्रिंग फ़ंक्शन को बताता है कि क्या उम्मीद है।
सबसे सरल तकनीक अन्य तर्कों की एक स्पष्ट गणना पास करना है (जो आमतौर पर सभी एक ही प्रकार हैं)। यह नीचे दिए गए कोड में वेरिएडिक फ़ंक्शन में प्रदर्शित किया जाता है, जो पूर्णांकों की एक श्रृंखला की राशि की गणना करता है, जहां किसी भी संख्या में पूर्णांक हो सकते हैं, लेकिन वह संख्या चर तर्क सूची से पहले एक तर्क के रूप में निर्दिष्ट होती है।
#include <stdio.h>
#include <stdarg.h>
/* first arg is the number of following int args to sum. */
int sum(int n, ...) {
int sum = 0;
va_list it; /* hold information about the variadic argument list. */
va_start(it, n); /* start variadic argument processing */
while (n--)
sum += va_arg(it, int); /* get and sum the next variadic argument */
va_end(it); /* end variadic argument processing */
return sum;
}
int main(void)
{
printf("%d\n", sum(5, 1, 2, 3, 4, 5)); /* prints 15 */
printf("%d\n", sum(10, 5, 9, 2, 5, 111, 6666, 42, 1, 43, -6218)); /* prints 666 */
return 0;
}
Va_list के अंत को निर्धारित करने के लिए टर्मिनेटर मान का उपयोग करना
किसी भी चर समारोह के साथ, फ़ंक्शन को पता होना चाहिए कि चर तर्कों की सूची की व्याख्या कैसे की जाए। "पारंपरिक" दृष्टिकोण (प्रिंटफ द्वारा printf
) सामने के तर्कों की संख्या निर्दिष्ट करना है। हालांकि, यह हमेशा एक अच्छा विचार नहीं है:
/* First argument specifies the number of parameters; the remainder are also int */
extern int sum(int n, ...);
/* But it's far from obvious from the code. */
sum(5, 2, 1, 4, 3, 6)
/* What happens if i.e. one argument is removed later on? */
sum(5, 2, 1, 3, 6) /* Disaster */
कभी-कभी यह एक स्पष्ट टर्मिनेटर जोड़ने के लिए और अधिक मजबूत होता है, जो POSIX execlp()
फ़ंक्शन द्वारा execlp()
। double
संख्याओं की एक श्रृंखला की गणना करने के लिए यहां एक और कार्य है:
#include <stdarg.h>
#include <stdio.h>
#include <math.h>
/* Sums args up until the terminator NAN */
double sum (double x, ...) {
double sum = 0;
va_list va;
va_start(va, x);
for (; !isnan(x); x = va_arg(va, double)) {
sum += x;
}
va_end(va);
return sum;
}
int main (void) {
printf("%g\n", sum(5., 2., 1., 4., 3., 6., NAN));
printf("%g\n", sum(1, 0.5, 0.25, 0.125, 0.0625, 0.03125, NAN));
}
अच्छा टर्मिनेटर मान:
- पूर्णांक (सभी सकारात्मक या गैर-नकारात्मक माना जाता है) -
0
या-1
- फ्लोटिंग पॉइंट प्रकार -
NAN
- सूचक प्रकार -
NULL
- प्रगणक प्रकार - कुछ विशेष मूल्य
`प्रिंटफ़ ()`-समान इंटरफ़ेस के साथ कार्यों को लागू करना
चर-लंबाई तर्क सूचियों का एक सामान्य उपयोग उन कार्यों को लागू करना है जो फ़ंक्शन के printf()
परिवार के आसपास एक पतली आवरण हैं। ऐसा ही एक उदाहरण त्रुटि रिपोर्टिंग कार्यों का एक सेट है।
errmsg.h
#ifndef ERRMSG_H_INCLUDED
#define ERRMSG_H_INCLUDED
#include <stdarg.h>
#include <stdnoreturn.h> // C11
void verrmsg(int errnum, const char *fmt, va_list ap);
noreturn void errmsg(int exitcode, int errnum, const char *fmt, ...);
void warnmsg(int errnum, const char *fmt, ...);
#endif
यह एक नंगे हड्डियों का उदाहरण है; ऐसे पैकेज अधिक विस्तृत हो सकते हैं। आम तौर पर, प्रोग्रामर या तो errmsg()
या warnmsg()
उपयोग verrmsg()
, जो स्वयं verrmsg()
आंतरिक रूप से उपयोग करते हैं। यदि कोई और अधिक करने की आवश्यकता के साथ आता है, हालांकि, तब उजागर verrmsg()
फ़ंक्शन उपयोगी होगा। जब तक आप इसके लिए एक की जरूरत है आप में लाने से बचें सकता है ( YAGNI - आपको इसकी आवश्यकता वाला नहीं हैं ), लेकिन जरूरत अंततः पैदा होगा (तुम जरूरत यह कर रहे हैं - YAGNI)।
errmsg.c
इस कोड को केवल मानक त्रुटि के लिए आउटपुट के लिए vfprintf()
फ़ंक्शन के लिए वैरेडिक तर्कों को अग्रेषित करने की आवश्यकता है। यह सिस्टम एरर मैसेज को रिपोर्ट करता है जो सिस्टम एरर नंबर ( errno
) को फंक्शन्स में पास करता है।
#include "errmsg.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void
verrmsg(int errnum, const char *fmt, va_list ap)
{
if (fmt)
vfprintf(stderr, fmt, ap);
if (errnum != 0)
fprintf(stderr, ": %s", strerror(errnum));
putc('\n', stderr);
}
void
errmsg(int exitcode, int errnum, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
verrmsg(errnum, fmt, ap);
va_end(ap);
exit(exitcode);
}
void
warnmsg(int errnum, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
verrmsg(errnum, fmt, ap);
va_end(ap);
}
errmsg.h
का उपयोग करना
अब आप उन कार्यों का उपयोग इस प्रकार कर सकते हैं:
#include "errmsg.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
char buffer[BUFSIZ];
int fd;
if (argc != 2)
{
fprintf(stderr, "Usage: %s filename\n", argv[0]);
exit(EXIT_FAILURE);
}
const char *filename = argv[1];
if ((fd = open(filename, O_RDONLY)) == -1)
errmsg(EXIT_FAILURE, errno, "cannot open %s", filename);
if (read(fd, buffer, sizeof(buffer)) != sizeof(buffer))
errmsg(EXIT_FAILURE, errno, "cannot read %zu bytes from %s", sizeof(buffer), filename);
if (close(fd) == -1)
warnmsg(errno, "cannot close %s", filename);
/* continue the program */
return 0;
}
यदि या तो open()
या read()
सिस्टम कॉल विफल हो जाता है, तो त्रुटि मानक त्रुटि के लिए लिखी जाती है और प्रोग्राम एग्जिट कोड से बाहर निकलता है। 1. यदि close()
सिस्टम कॉल विफल हो जाता है, तो त्रुटि केवल एक चेतावनी संदेश के रूप में मुद्रित होती है, और कार्यक्रम जारी है।
printf()
प्रारूपों के सही उपयोग की जाँच करना
यदि आप जीसीसी (जीएनयू सी कंपाइलर, जो कि जीएनयू कंपाइलर कलेक्शन का हिस्सा है) का उपयोग कर रहे हैं, या क्लैंग का उपयोग कर रहे हैं, तो आप कंपाइलर की जाँच कर सकते हैं कि आप जो एरर मैसेज फ़ंक्शंस में पास हुए हैं, वे तर्क मेल खाते हैं जो printf()
अपेक्षा करता है। चूंकि सभी संकलक विस्तार का समर्थन नहीं करते हैं, इसलिए इसे सशर्त रूप से संकलित करने की आवश्यकता है, जो कि थोड़ा सा है। हालांकि, यह जो सुरक्षा देता है वह प्रयास के लायक है।
सबसे पहले, हमें यह जानना होगा कि कैसे पता लगाया जाए कि कंपाइलर जीसीसी है या क्लैंग जीसीसी का अनुकरण कर रहा है। इसका उत्तर यह है कि GCC यह इंगित करने के लिए __GNUC__
को परिभाषित करता है।
विशेषताओं के बारे में जानकारी के लिए सामान्य फ़ंक्शन विशेषताएँ देखें - विशेष रूप से format
विशेषता।
फिर से लिखा गया errmsg.h
#ifndef ERRMSG_H_INCLUDED
#define ERRMSG_H_INCLUDED
#include <stdarg.h>
#include <stdnoreturn.h> // C11
#if !defined(PRINTFLIKE)
#if defined(__GNUC__)
#define PRINTFLIKE(n,m) __attribute__((format(printf,n,m)))
#else
#define PRINTFLIKE(n,m) /* If only */
#endif /* __GNUC__ */
#endif /* PRINTFLIKE */
void verrmsg(int errnum, const char *fmt, va_list ap);
void noreturn errmsg(int exitcode, int errnum, const char *fmt, ...)
PRINTFLIKE(3, 4);
void warnmsg(int errnum, const char *fmt, ...)
PRINTFLIKE(2, 3);
#endif
अब, यदि आप गलती करते हैं जैसे:
errmsg(EXIT_FAILURE, errno, "Failed to open file '%d' for reading", filename);
(जहां %d
को %s
होना चाहिए), तब कंपाइलर शिकायत करेगा:
$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes \
> -Wold-style-definition -c erruse.c
erruse.c: In function ‘main’:
erruse.c:20:64: error: format ‘%d’ expects argument of type ‘int’, but argument 4 has type ‘const char *’ [-Werror=format=]
errmsg(EXIT_FAILURE, errno, "Failed to open file '%d' for reading", filename);
~^
%s
cc1: all warnings being treated as errors
$
एक प्रारूप स्ट्रिंग का उपयोग करना
एक प्रारूप स्ट्रिंग का उपयोग करके अपेक्षित संख्या तर्क और बाद के वैरिएबल तर्कों के बारे में इस तरह से जानकारी दी जाती है जैसे कि एक स्पष्ट गणना तर्क या एक टर्मिनेटर मान की आवश्यकता से बचने के लिए।
शो नीचे दिए गए उदाहरण मानक लपेटता है कि आ समारोह printf()
समारोह, केवल प्रकार के variadic तर्क के उपयोग की अनुमति के लिए char
, int
और double
(दशमलव चल बिन्दु प्रारूप में)। यहां, printf()
, रैपिंग फ़ंक्शन का पहला तर्क प्रारूप स्ट्रिंग है। जैसा कि प्रारूप स्ट्रिंग को पार्स किया जाता है, फ़ंक्शन यह निर्धारित करने में सक्षम होता है कि क्या कोई अन्य वैरेडिक तर्क अपेक्षित है और यह किस प्रकार का होना चाहिए।
#include <stdio.h>
#include <stdarg.h>
int simple_printf(const char *format, ...)
{
va_list ap; /* hold information about the variadic argument list. */
int printed = 0; /* count of printed characters */
va_start(ap, format); /* start variadic argument processing */
while (*format != '\0') /* read format string until string terminator */
{
int f = 0;
if (*format == '%')
{
++format;
switch(*format)
{
case 'c' :
f = printf("%d", va_arg(ap, int)); /* print next variadic argument, note type promotion from char to int */
break;
case 'd' :
f = printf("%d", va_arg(ap, int)); /* print next variadic argument */
break;
case 'f' :
f = printf("%f", va_arg(ap, double)); /* print next variadic argument */
break;
default :
f = -1; /* invalid format specifier */
break;
}
}
else
{
f = printf("%c", *format); /* print any other characters */
}
if (f < 0) /* check for errors */
{
printed = f;
break;
}
else
{
printed += f;
}
++format; /* move on to next character in string */
}
va_end(ap); /* end variadic argument processing */
return printed;
}
int main (int argc, char *argv[])
{
int x = 40;
int y = 0;
y = simple_printf("There are %d characters in this sentence", x);
simple_printf("\n%d were printed\n", y);
}