如果我不知道free()
的长度,我如何freeme
稍后freeme
中的变量?我有一个手柄,但我不知道长度。我可以添加一个计数器,然后在调用它的函数的末尾循环它,但通常比最明显的方式更好的样式。我宁愿在函数中没有另一个变量。我已经对包括booleans
持怀疑态度了。
bool parse(bool b1, int i, int *j, int *e, char **ptr, int q, char **pString, char *str,
char **freeme) {
for (*e = 0; *(ptr + q + *e); (*e)++) {
b1 = true;
if (*(ptr + *e + q)) {
str = concat(*pString, *(ptr + *e + q));
*pString = concat(str, " ");
free(str);
freeme[*e] = *pString;
}
*j = *e;
}
return b1;
}
如果我理解正确,那么当我完成时,free()
数组中的所有*pString
都必须freeme
。 concat
看起来像这样,我从SO的回答中得到了它。 How do I concatenate two strings in C?
char *concat(char *s1, char *s2) {
char *result = malloc(strlen(s1) + strlen(s2) + 1);//+1 for the zero-terminator
//in real code you would check for errors in malloc here
if (result == NULL) {
fprintf(stderr, "malloc failed!\n");
return (char *) '0';
}
strcpy(result, s1);
strcat(result, s2);
return result;
}
答案 0 :(得分:2)
学习C的一个重要方面是学习如何在抽象方面做出好的选择。词法分析器/解析器本身就很复杂,不应该使用原始的低级指针和字符串操作,这些操作容易出错并降低可读性。
因此,不是编写解析代码,而是首先要编写一些强大且可测试的抽象(就C中的抽象而言......)。
你的解析函数的StringList
参数,例如对我来说就像一个StringList。因此,首先使用列表的常规操作自己编写一个StringList模块是值得的。
此外,特别是在解析器的上下文中,字符串和其他动态分配的对象通常保存在多个数据结构中。因此,问题出现了,哪个容器“拥有”该实例。即谁能释放它?这将是另一个基本方面,如果正确处理可以帮助以更简洁和更不容易出错的方式编写解析器本身。
有了所有这些,你的问题就这样过时了,因为bool parse(InputIterator * source_iter, StringList * tokens )
{
char currentToken[MAX_TOKEN_LENGTH];
/* ... */
for( ; !InputIterator_end(source_iter); InputIterator_advance(source_iter, 1)) {
char current = InputIterator_current(source_iter);
if(isws(current)) {
StringList_append( tokens, currentToken);
skip_whitespaces(source_iter); /* another helper function of yours*/
}
else {
string_append_char( currentToken, sizeof(currentToken), current); // no need to write this logic 100 times all over your parser.
}
}
return true;
}
模块将解决那些细节而不是你的解析器代码。
最后,至少,您可以选择现成的抽象方式,因为它们之前已被编写和使用了数千次。 FreeBSD内核代码是纯C语言,代码非常清晰且经过良好测试,我相信你会找到合适的代码。
应用此建议可能会使您的代码看起来更像这样:
<?php
session_start();
include 'db.php';
if (!isset($_SESSION['id'])) {
header("location:login.php");
}
//echo $_SESSION['id'].$_SESSION['username'];
?>
<!DOCTYPE HTML>
<!--
Landed by HTML5 UP
html5up.net | @n33co
Free for personal and commercial use under the CCA 3.0 license (html5up.net/license)
-->
<html>
<head>
<title>Profile</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="homeass/assets/css/main.css" />
</head>
<body>
<div id="page-wrapper">
<!-- Header -->
<header id="header">
<h1 id="logo"><a href="profile.html">Profile</a></h1>
<nav id="nav">
<ul>
<li><a href="home.php">Home</a></li>
<li>
<a href="#">News Feed</a>
</li>
</ul>
</li>
<li><a href="profile.php">Profile</a></li>
<li><a href="logout.php" class="button special">Log Out</a></li>
</ul>
</nav>
</header>
<!-- Main -->
<div id="main" class="wrapper style1">
<div class="container">
<header class="major">
<h2>Profile</h2>
<p>Edit your stored information</p>
</header>
<!-- Form -->
<section>
<?php
$uid = $_SESSION['id'];
$getdata = "SELECT name,email,bloodgroup,mobile,relative_mob1,relative_mob2,message FROM users WHERE id = ?";
$stmtget = $con->prepare($getdata);
$stmtget->bind_param("i",$uid);
$stmtget->execute();
$stmtget->bind_result($gname,$gemail,$gbloodgroup,$gmobile,$gremob1,$gremob2,$gmes);
$stmtget->fetch();
$stmtget->store_result();
$stmtget->close();
//echo $gname;
?>
<?php
if (isset($_POST['btn-upload'])) {
// echo $_SESSION['username'];
$file = $_SESSION['username']."-".$_FILES['file']['name'];
$file_loc = $_FILES['file']['tmp_name'];
$folder="uploads/";
if (move_uploaded_file($file_loc,$folder.$file)){
$sql = "UPDATE users SET pic = ? WHERE id = ?";
$stmti = $con->prepare($sql);
$stmti->bind_param("si",$file,$uid);
$stmti->execute();
}
}
$get = "SELECT pic FROM users WHERE id = ?";
$stmtg = $con->prepare($get);
$stmtg->bind_param("i",$uid);
$stmtg->execute();
$stmtg->bind_result($img);
$stmtg->fetch();
//echo '<img src="uploads/'.$img.'" width="50" height="50"/>';
?>
<section>
<h3>Image</h3>
<h4>Profile Pic</h4>
<div class="box alt">
<div class="4u 6u(xsmall)"><span class="image fit"><?php echo '<img src="uploads/'.$img.'" width="100" height="200"/>'; ?></span></div>
</div>
</div>
</section>
<div class="probut">
<form action="profile.php" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<button type="submit" name="btn-upload">upload</button>
</form>
</div>
<form method="post" action="profile.php">
<div class="row uniform 50%">
<!-- <div class="6u 12u$(xsmall)"><input type="file" name="file"/></div> -->
<h4>Name</h4>
<div class="6u 12u$(xsmall)">
<input type="text" name="name" value="<?php echo $gname ?>" placeholder="Name" />
</div>
<h4>Email</h4>
<div class="6u$ 12u$(xsmall)">
<input type="email" name="email" id="email" value="<?php echo $gemail ?>" placeholder="Email" />
</div>
<h4>Blood Group</h4>
<div class="6u$ 12u$(xsmall)">
<input type="text" name="bloodgroup" id="name" value="<?php echo $gbloodgroup ?>" placeholder="Blood Group" />
</div>
<h4>Your Mobile number</h4>
<div class="6u 12u$(xsmall)">
<input type="tel" name="mobile" id="name" value="<?php echo $gmobile ?>" placeholder="Mobile Number" />
</div>
<h4>Relatives Mob number 1</h4>
<div class="6u 12u$(xsmall)">
<input type="tel" name="remob1" id="name" value="<?php echo $gremob1 ?>" placeholder="Mobile Number" />
</div>
<h4>Relatives Mob number 2</h4>
<div class="6u 12u$(xsmall)">
<input type="tel" name="remob2" id="name" value="<?php echo $gremob2 ?>" placeholder="Mobile Number" />
</div>
<h4>Default Message</h4>
<div class="12u$">
<!-- <textarea name="message" id="name" value="value" placeholder="" rows="6"></textarea> -->
<input type="tel" name="message" id="name" value="<?php echo $gmes ?>" placeholder="Message" />
</div>
<div class="12u$">
<ul class="actions">
<li><input type="submit" name="submitttt" value="Save" class="special" /></li>
<li><input type="reset" value="Reset" /></li>
</ul>
</div>
</div>
</form>
<?php
if (isset($_POST['submitttt'])) {
$nameu = $_POST['name'];
$bloodgroupu = $_POST['bloodgroup'];
$mobileu = $_POST['mobile'];
$remob1u = $_POST['remob1'];
$remob2u = $_POST['remob2'];
$messageu = $_POST['message'];
$newsql = "UPDATE users SET name = '".$nameu."',bloodgroup = '".$bloodgroupu."',mobile = '".$mobileu."',relative_mob1 = '".$remob1u."',relative_mob2 = '".$remob2u."',message = '".$messageu."' WHERE id = ".$uid."" ;
if (mysqli_query($con,$newsql)) {
echo "Updated";
} else{
echo "Not updated";
echo "query failed: (" . $con->errno . ") " . $con->error;
}
echo '<h1>'.$uid.'</h1>';
echo $newsql;
/*$up = "UPDATE users SET name = ?,bloodgroup = ?,mobile = ?,relative_mob1 = ?,relative_mob2 = ?,message = ? WHERE id = ?";
$stmtuppp = $con->prepare($up);
$stmtuppp->bind_param("ssssssi",$nameu,$bloodgroupu,$mobileu,$remob1u,$remob2u,$messageu,$uid);
$stmtuppp->execute();
echo '<script>alert("updated")</script>';
echo "<meta http-equiv='refresh' content='0'>";*/
/*echo $nameu.'<br>';
echo $bloodgroupu;
echo $mobileu;
echo $remob1u;
echo $remob2u;
echo $messageu;*/
}
?>
</section>
</div>
</div>
<!-- Footer -->
<footer id="footer">
<ul class="icons">
<li><a href="#" class="icon alt fa-twitter"><span class="label">Twitter</span></a></li>
<li><a href="#" class="icon alt fa-facebook"><span class="label">Facebook</span></a></li>
<li><a href="#" class="icon alt fa-envelope"><span class="label">Email</span></a></li>
</ul>
<ul class="copyright">
<li>© Yelp Mee. All rights reserved.</li><li>Design: <a>3rd Year CSE</a></li>
</ul>
</footer>
</div>
<!-- Scripts -->
<script src="homeass/assets/js/jquery.min.js"></script>
<script src="homeass/assets/js/jquery.scrolly.min.js"></script>
<script src="homeass/assets/js/jquery.dropotron.min.js"></script>
<script src="homeass/assets/js/jquery.scrollex.min.js"></script>
<script src="homeass/assets/js/skel.min.js"></script>
<script src="homeass/assets/js/util.js"></script>
<!--[if lte IE 8]><script src="assets/js/ie/respond.min.js"></script><![endif]-->
<script src="homeass/assets/js/main.js"></script>
</body>
</html>
根据我的经验,这种C编程风格运行良好,产生可测试的小函数,并且与“一体化”方法相比更具可读性。
答案 1 :(得分:2)
如果我正确读取了你的解析函数,它似乎正在迭代ptr
指定的字符串列表。列表本身为空终止,列表中的起始偏移量为索引q
。
解析函数的目标似乎是生成一个输出字符串,该输出字符串是所有输入字符串的串联,但在每个字符串之间插入了分隔符字符串str
和space
。如下所示:
result = ""
for each string s in ptr:
result += s
result += delimiter
result += " "
return result;
令人困惑的是,parse
函数的一些输入参数实际上只是局部变量或根本没有使用。这包括e',
我, and
b1`。
那么是什么让你的问题变得困难的是将每个字符串连接在一起并尝试跟踪以后释放每个临时分配的内存管理。你真正需要的是一个字符串类。但这是C,而不是C ++。幸运的是,我们可以通过结构类型和一些有价值的辅助函数来克服这个问题。
我建议让我们从为串联优化的有价值的字符串类开始。让我们定义一个&#34; SimpleString&#34;如下:
typedef struct _SimpleString
{
char* str; // the actual null terminated string
size_t length; // length of psz, not including null char
size_t allocated; // amount of memory malloc'd including room for null char
} SimpleString;
此结构的str
是原始字符串指针。
现在让我们创建一个简单的构造函数&#34;函数来创建一个字符串:
SimpleString* create_string()
{
SimpleString* s = (SimpleString*)malloc(sizeof(SimpleString));
if (s == NULL)
{
return NULL; // out of memory
}
s->str = malloc(1);
if (s->str == NULL)
{
free(s);
return NULL;
}
s->str[0] = '\0';
s->length = 0;
s->allocated = 1;
return s;
}
以上功能将&#34; malloc&#34; SimpleString的一个实例并返回指向它的指针。 SimpleString的内部成员str
初始化为空字符串。
现在每当我们想要连接到这个字符串时,我们可以使用我们的帮助函数,如下所示:
int concat_string(SimpleString* s, const char* p)
{
size_t needed = 0;
size_t p_len = p ? strlen(p) : 0;
if (p == NULL)
{
return 0;
}
if (p_len == 0)
{
// nothing to do
return 1;
}
if (s->str)
{
needed += s->length;
}
needed += p_len;
needed += 1; // for null char
if (needed > s->allocated)
{
size_t newallocation = needed * 2; // allocate more than needed so that we don't have to reallocate and re-copy the buffer on each call to concat_string
char* newstring = malloc(newallocation);
if (newstring == NULL)
{
// out of memory
return 0;
}
newstring[0] = '\0';
s->allocated = newallocation;
if (s->str && (s->length > 0))
{
memcpy(newstring, s->str, s->length);
}
free(s->str);
s->str = newstring;
}
memcpy(s->str + s->length, p, p_len);
s->str[s->length + p_len] = '\0';
s->length += p_len;
return 1;
}
上面的函数将在字符串增长时重新分配额外的内存(并释放旧内存)。
现在最后一个帮助函数释放字符串:
void release_string(SimpleString* s)
{
if (s)
{
free(s->str);
free(s);
}
}
现在我们可以大大简化您的解析功能,如下所示:
SimpleString* parse(char **list, int q, const char *delimiter)
{
SimpleString* result = NULL;
char *current = list[q];
int count = 0;
while (current != NULL)
{
if (result == NULL)
{
result = create_string();
if (result == NULL)
{
// error! out of memory
break;
}
}
concat_string(result, current);
concat_string(result, delimiter);
concat_string(result, " ");
count++;
current = list[q + count];
}
return result;
}
在上述功能中,我将您的ptr
参数重命名为list
,将str
参数重命名为delimiter
。我将q
单独留作列表中的初始偏移量。
我怀疑你真的只想连接除最后一次迭代之外的所有尾随空格。如果是这样的话,我会将其作为练习留给你。
在原始实现中,您使用参数j
来指示使用的字符串计数并作为out参数返回。你可以轻松地添加它。 (例如*j = count;
)
样本用法:
int main()
{
char* list[] = { "Mecury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune", NULL };
SimpleString* result = parse(list, 0, ",");
printf("%s\n", result->str);
release_string(result);
return 0;
}
答案 2 :(得分:1)
您可以引入另一个参数来计算freeme
中的元素。
bool parse(bool b1, int i, int *j, int *e, char **ptr, int q, char **pString, char *str,
char **freeme, int *freeme_len) {
for (*e = 0; *(ptr + q + *e); (*e)++) {
b1 = true;
if (*(ptr + *e + q)) {
str = concat(*pString, *(ptr + *e + q));
*pString = concat(str, " ");
free(str);
//freeme[*e] = *pString;
freeme[(*freeme_len)++] = *pString;
}
*j = *e;
}
return b1;
}
调用parse()后,迭代所有freeme
元素并释放它们。
void main() {
//The following line outlines that freeme is allocated somewhere and it's type is char**
//and should not be taken literally. It's size should be calculated, according
//to the situation, or should be dynamically resized during operations.
char **freeme = malloc(1000 * sizeof(char*));
//freeme_len should be initialized to 0 before first calling to parse().
int freeme_len = 0;
//here follows some program code, that we don't know about, which declares and
//initializes all remaining variables
parse(b1, i, &j, &e, ptr, q, &pString, str, freeme, &freeme_len);
//After calling, free all
for(i=0; i<freeme_len; i++) {
free( freeme[i] );
}
free(freeme);
}