WPF中的常用布局
一 写在开头
1.1 写在开头
微软是一家伟大的公司。评价一门技术的好坏得看具体的需求,没有哪门技术是面面俱到地好,应该抛弃对微软和微软的技术的偏见。
1.2 本文内容
本文主要内容为WPF中的常用布局,大部分内容转载至https://blog.csdn.net/woshisunjiale/article/details/54136323,代码片段可能有所不同。
二 WPF中的常用布局
因为项目需要,所以得学习WPF开发。WPF使软件界面和逻辑相分离,手写xaml进行程序UI的开发是件很惬意的事情。从这点来说WPF要比Qt和GTK+要好。当然了,如果其能跨平台甚至开源那就更好了。但是,商业有商业自身的规律。
2.1 Canvas布局
Canvas是一个类似于坐标系的面板,所有的元素通过设置坐标来决定其在坐标系中的位置。具体表现为使用Left、Top、Right、 Bottom附加属性在Canvas中定位控件。
1 <Window x:Class="WPF_Layout.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="WPF-Layout" Height="350" Width="525"> 5 <Grid> 6 <Canvas> 7 <Button Canvas.Left="50" Canvas.Top="50" Content="Button 1"></Button> 8 <Button Canvas.Right="50" Canvas.Top="50" Content="Button 2"></Button> 9 <Button Canvas.Left="50" Canvas.Bottom="50" Content="Button 3"></Button> 10 <Button Canvas.Right="50" Canvas.Bottom="50" Content="Button 4"></Button> 11 </Canvas> 12 </Grid> 13 </Window>
注意:如果同时设置 Canvas.Left="50"Canvas.Right="50",则以Canvas.Left="50"为准。如果同时设置Canvas.Top="50" Canvas.Bottom="50",则以Canvas.Top ="50"为准。(别这么丧心病狂地同时写Left和Right,请遵循基本的编程逻辑)
2.2 StackPanel布局
StackPanel将控件按照行或列来顺序排列,但不会换行。通过设置面板的Orientation属性设置了两种排列方式:横排(Horizontal默认的)和竖排(Vertical),默认为竖排(Vertical)。
1 <Window x:Class="WPF_Layout.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="WPF-Layout" Height="350" Width="525"> 5 <Grid> 6 <StackPanel Name="stackpanel1" Orientation="Horizontal"> 7 <Button Content="Button1"></Button> 8 <Button Content="Button2"></Button> 9 <Button Content="Button3"></Button> 10 </StackPanel> 11 <StackPanel Name="stackpanel2" Orientation="Vertical"> 12 <Button Content="Button4"></Button> 13 <Button Content="Button5"></Button> 14 <Button Content="Button6"></Button> 15 </StackPanel> 16 <StackPanel Name="stackpanel3" Orientation="Horizontal" FlowDirection="RightToLeft"> 17 <Button Content="Button7"></Button> 18 <Button Content="Button8"></Button> 19 <Button Content="Button9"></Button> 20 </StackPanel> 21 </Grid> 22 </Window>
注意:Orientation="Horizontal"时,设置FlowDirection属性为RightToLeft,,则元素将从右向左排列。
2.3 DockPanel布局
DockPanel支持让元素简单地停靠在整个面板的某一条边上,然后拉伸元素以填满全部宽度或高度。它也支持让一个元素填充其他已停靠元素没有占用的剩余空间。
DockPanel有一个Dock附加属性,因此子元素用4个值来控制她们的停靠:Left、Top、Right、Bottom。Dock没有Fill值。作为替代,最后的子元素将加入一个DockPanel并填满所有剩余的空间,除非DockPanel的LastChildFill属性为false,它将朝某个方向停靠。
1 <Window x:Class="WPF_Layout.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="WPF-Layout" Height="350" Width="525"> 5 <Grid> 6 <DockPanel> 7 <Button Content="上" DockPanel.Dock="Left"></Button> 8 <Button Content="下" DockPanel.Dock="Bottom"></Button> 9 <Button Content="左" DockPanel.Dock="Left"></Button> 10 <Button Content="右" DockPanel.Dock="Right"></Button> 11 </DockPanel> 12 </Grid> 13 </Window>
设置LastChildFill属性为false
1 <Window x:Class="WPF_Layout.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="WPF-Layout" Height="350" Width="525"> 5 <Grid> 6 <DockPanel LastChildFill="False"> 7 <Button Content="上" DockPanel.Dock="Left"></Button> 8 <Button Content="下" DockPanel.Dock="Bottom"></Button> 9 <Button Content="左" DockPanel.Dock="Left"></Button> 10 <Button Content="右" DockPanel.Dock="Right"></Button> 11 </DockPanel> 12 </Grid> 13 </Window>
2.4 WrapPanel布局
WrapPanel布局面板将各个控件按照一定方向罗列,当长度或高度不够时自动调整进行换行换列。
Orientation="Horizontal"时各控件从左至右罗列,当面板长度不够时,子控件就会自动换行,继续按照从左至右的顺序排列。
Orientation="Vertical"时各控件从上至下罗列,当面板高度不够时,子控件就会自动换列,继续按照从上至下的顺序排列。
1 <Window x:Class="WPF_Layout.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="WPF-Layout" Height="350" Width="525"> 5 <Grid> 6 <WrapPanel Orientation="Horizontal"> 7 <Button Content="Button 150" Width="150"></Button> 8 <Button Content="Button 200" Width="200"></Button> 9 <Button Content="Button 150" Width="150"></Button> 10 <Button Content="Button 200" Width="200"></Button> 11 <Button Content="Button 150" Width="150"></Button> 12 <Button Content="Button 200" Width="200"></Button> 13 <Button Content="Button 150" Width="150"></Button> 14 </WrapPanel> 15 </Grid> 16 </Window>
2.5 Grid布局
Grid允许我们通过自定义行列来进行布局,这类似于表格.通过定义Grid的RowDifinitions和ColumnDifinitions来实现对于表格行和列的定义,元素根据附加属性Grid.Row和Grid.Column确定自己的位置。
1)Grid的列宽与行高可采用固定、自动、按比列三种方式定义
第一种,固定长度——值为一个确定的数字
第二种,自动长度——值为Auto,实际作用就是取实际控件所需的最小值
第三种,比例长度——*表示占用剩余的全部宽度;两行都是*,将平分剩余宽度;一个2*,一个*,则前者占剩余全部宽度的2/3,后者占1/3;依此类推
1 <Window x:Class="WPF_Layout.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="WPF-Layout" Height="350" Width="525"> 5 <Grid> 6 <Grid.RowDefinitions> 7 <RowDefinition Height="40"></RowDefinition> 8 <RowDefinition Height="Auto"></RowDefinition> 9 <RowDefinition Height="2*"></RowDefinition> 10 <RowDefinition Height="*"></RowDefinition> 11 </Grid.RowDefinitions> 12 <Button Grid.Row="0" Content="Button 1"></Button> 13 <Button Grid.Row="1" Content="Button 2"></Button> 14 <Button Grid.Row="2" Content="Button 3"></Button> 15 <Button Grid.Row="3" Content="Button 4"></Button> 16 </Grid> 17 </Window>
2) 合并行或列
1 <Window x:Class="WPF_Layout.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="WPF-Layout" Height="350" Width="525"> 5 <Grid> 6 <Grid.RowDefinitions> 7 <RowDefinition Height="40"></RowDefinition> 8 <RowDefinition Height="Auto"></RowDefinition> 9 <RowDefinition Height="2*"></RowDefinition> 10 <RowDefinition Height="*"></RowDefinition> 11 </Grid.RowDefinitions> 12 <Button Grid.Row="0" Content="Button 1"></Button> 13 <Button Grid.Row="1" Content="Button 2"></Button> 14 <Button Grid.Row="2" Grid.RowSpan="2" Content="Button 3"></Button> 15 </Grid> 16 </Window>
3)GridSplitter重新分布Grid控件的列间距或行间距。(类似于WinForm中SplitContainer)
1 <Window x:Class="WPF_Layout.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="WPF-Layout" Height="350" Width="525"> 5 <Grid> 6 <Grid.RowDefinitions> 7 <RowDefinition Height="*"></RowDefinition> 8 <RowDefinition Height="3"></RowDefinition> 9 <RowDefinition Height="*"></RowDefinition> 10 </Grid.RowDefinitions> 11 <Button Grid.Row="0" Content="Button"></Button> 12 <GridSplitter Grid.Row="1" HorizontalAlignment="Stretch"></GridSplitter> 13 <Button Grid.Row="2" Content="Button"></Button> 14 </Grid> 15 </Window>
2.6 UniformGrid布局
UniformGrid就是Grid的简化版,每个单元格的大小相同,不需要定义行列集合。每个单元格始终具有相同的大小,每个单元格只能容纳一个控件。
若不设置RowsColums,则按照定义在其内部的元素个数,自动创建行列,并通常保持相同的行列数。若只设置Rows则固定行数,自动扩展列数。若只设置Colums则固定列数,自动扩展行数。
UniformGrid 中没有Row和Column附加属性,也没有空白单元格。
1 <Window x:Class="WPF_Layout.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="WPF-Layout" Height="350" Width="525"> 5 <Grid> 6 <UniformGrid> 7 <Button Content="Button"></Button> 8 <Button Content="Button"></Button> 9 <Button Content="Button"></Button> 10 <Button Content="Button"></Button> 11 <Button Content="Button"></Button> 12 <Button Content="Button"></Button> 13 <Button Content="Button"></Button> 14 </UniformGrid> 15 </Grid> 16 </Window>
1 <Window x:Class="WPF_Layout.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="WPF-Layout" Height="350" Width="525"> 5 <Grid> 6 <UniformGrid Columns="2"> 7 <Button Content="Button"></Button> 8 <Button Content="Button"></Button> 9 <Button Content="Button"></Button> 10 <Button Content="Button"></Button> 11 <Button Content="Button"></Button> 12 <Button Content="Button"></Button> 13 <Button Content="Button"></Button> 14 </UniformGrid> 15 </Grid> 16 </Window>
2.7 ScrollViewer布局
ScrollViewer是带有滚动条的面板。在ScrollViewer中只能有一个子控件,若要显示多个子控件,需要将一个附加的 Panel控件放置在父 ScrollViewer中。然后可以将子控件放置在该控件中。
HorizontalScrollBarVisibility水平滚动条是否显示默认为Hidden
VerticalScrollBarVisibility垂直滚动条是否显示 默认为Visible。
一般我们都会设置 HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
意思是:当内容超出可视范围时,才显示横向/纵向滚动条
1 <Window x:Class="WPF_Layout.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="WPF-Layout" Height="350" Width="525"> 5 <Grid> 6 <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"> 7 <Button Content="Button" Width="800" Height="800"></Button> 8 </ScrollViewer> 9 </Grid> 10 </Window>
2.8 ViewBox布局
Viewbox的作用是拉伸或延展位于其中的组件,以填满可用空间。在Viewbox中只能有一个子控件,若要显示多个子控件,需要将一个附加的Panel控件放置在父Viewbox中。然后可以将子控件放置在该控件中。
常用属性:
Stretch:获取或设置拉伸模式以决定该组件中的内容以怎样的形式填充该组件的已有空间。具体设置值如下:
None:不进行拉伸,按子元素设置的长宽显示。
Uniform:按原比例缩放子元素,使得一边不足,另一边恰好填充
Fill:缩放子元素,使得子元素的长变为Viewbox的长,宽变为Viewbox的宽
UniformToFill:按原比例缩放子元素,使得子元素一边恰好填充,另一边超出Viewbox的区域
Stretch默认值为Uniform。
1 <Window x:Class="WPF_Layout.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="WPF-Layout" Height="350" Width="525"> 5 <Grid> 6 <Grid.RowDefinitions> 7 <RowDefinition> 8 9 </RowDefinition> 10 </Grid.RowDefinitions> 11 <Grid.ColumnDefinitions> 12 <ColumnDefinition> 13 14 </ColumnDefinition> 15 </Grid.ColumnDefinitions> 16 <Viewbox Grid.Row="0" Grid.Column="0" Stretch="None"> 17 <Button Width="100" Height="50" Content="None"></Button> 18 </Viewbox> 19 <Viewbox Grid.Row="0" Grid.Column="1" Stretch="Uniform"> 20 <Button Width="100" Height="50" Content="Uniform"></Button> 21 </Viewbox> 22 <Viewbox Grid.Row="1" Grid.Column="0" Stretch="Fill"> 23 <Button Width="100" Height="50" Content="Fill"></Button> 24 </Viewbox> 25 <Viewbox Grid.Row="1" Grid.Column="1" Stretch="UniformToFill"> 26 <Button Width="100" Height="50" Content="UniformToFill"></Button> 27 </Viewbox> 28 </Grid> 29 </Window>
2.9 Border
Border 是一个装饰的控件,此控件用于绘制边框及背景,在Border中只能有一个子控件,若要显示多个子控件,需要将一个附加的Panel控件放置在父Border中。然后可以将子控件放置在该 Panel控件中。
常用属性:
Background: 背景色 ;
BorderBrush: 边框色 ;
BorderThickness: 边框宽度;
CornerRadius: 各个角 圆的半径;
1 <Window x:Class="WPF_Layout.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="WPF-Layout" Height="350" Width="525"> 5 <Grid> 6 <Border Background="YellowGreen" BorderBrush="Black" BorderThickness="0, 2, 4, 6" CornerRadius="0, 10, 20, 30"></Border> 7 </Grid> 8 </Window>
三 精确定位的常用属性
在设计UI时,WPF为我们提供了一些属性用于精确定位元素,其中最常用的有三个:Alignment(包括水平,垂直),Margin,Padding,具体用法如下:
HorizontalAlignment: 子元素在水平方向的对齐方式,有左对齐,右对齐,中间对齐,拉伸填充等四种方式。
VerticalAlignment:子元素在垂直方向的对齐方式,有顶端对齐,底部对齐,中间对齐,拉伸填充等四种方式。
Margin:用于指定元素与其父级或同级之间的距离,包括上下左右四个值。也可通过使用 Margin="20"同时指定四个值。
Padding:用于指定元素与其子级之间的距离,包括上下左右四个值。也可通过使用Padding="20"同时指定四个值。
1 <Window x:Class="WPF_Layout.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="WPF-Layout" Height="350" Width="525"> 5 <Grid> 6 <Grid.RowDefinitions> 7 <RowDefinition> 8 9 </RowDefinition> 10 <RowDefinition> 11 12 </RowDefinition> 13 </Grid.RowDefinitions> 14 <Button Grid.Row="0" Content="Center" HorizontalAlignment="Center"></Button> 15 <Button Grid.Row="1" Content="Left" HorizontalAlignment="Left"></Button> 16 <Button Grid.Row="2" Content="Right" HorizontalAlignment="Right"></Button> 17 <Button Grid.Row="3" Content="Stretch" HorizontalAlignment="Stretch"></Button> 18 </Grid> 19 </Window>
1 <Window x:Class="WPF_Layout.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="WPF-Layout" Height="350" Width="525"> 5 <Grid> 6 <Border Background="YellowGreen"> 7 <Button Margin="0, 50, 100, 150"></Button> 8 </Border> 9 </Grid> 10 </Window>
1 <Window x:Class="WPF_Layout.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="WPF-Layout" Height="350" Width="525"> 5 <Grid> 6 <Border Background="YellowGreen" Padding="0, 50, 100, 150"> 7 <Button ></Button> 8 </Border> 9 </Grid> 10 </Window>
栈的实现
一 写在开头
1.1 本文内容
数据结构栈的实现。
二 栈的实现
不多说了,请直接看代码。
1 // stack.h 2 3 /* 为了使用bool */ 4 #include <stdbool.h> 5 6 #ifndef _H_STACK_H 7 #define _H_STACK_H 8 9 /* 数据类型 */ 10 typedef char type; 11 12 /* 栈结点 */ 13 typedef struct tsNode 14 { 15 type data; 16 struct tsNode *next; 17 } Node; 18 19 /* 栈类型 */ 20 typedef struct tsStack 21 { 22 Node *top; 23 unsigned int size; 24 } Stack; 25 26 /* 初始化Stack */ 27 Stack * 28 StackInit(void); 29 30 /* 判断栈是否为空。为空返回true,否则返回false */ 31 bool 32 IsEmpty(Stack *); 33 34 /* 入栈操作 */ 35 void 36 StackPush(Stack *, type); 37 38 /* 出栈操作 */ 39 type 40 StackPop(Stack *); 41 42 /* 取得栈顶元素 */ 43 type 44 GetTop(Stack *); 45 #endif
1 // stack.c 2 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <stdbool.h> 6 #include "stack.h" 7 8 /* 功能:栈初始化 9 * 输入:无 10 * 输出:一个指向Stack型的指针,指针所指对象已被初始化 11 */ 12 Stack * 13 StackInit(void) 14 { 15 Stack *s = NULL; 16 17 s = (Stack *)malloc(sizeof(Stack)); 18 if (s == NULL) 19 exit(EXIT_FAILURE); 20 s->top = NULL; 21 s->size = 0; 22 return s; 23 } 24 25 /* 功能:判断栈是否为空。为空返回true,否则返回false 26 * 输入:一个指向Stack型的指针 27 * 输出:一个bool型变量 28 */ 29 bool 30 IsEmpty(Stack *s) 31 { 32 /* s未被初始化 */ 33 if (s == NULL) 34 exit(EXIT_FAILURE); 35 else 36 return ((s->size) == 0); 37 } 38 39 /* 功能:入栈操作 40 * 输入:一个指向Stack型的指针,一个type型待入栈变量 41 * 输出:无 42 */ 43 void 44 StackPush(Stack *s, type x) 45 { 46 Node *temp = (Node *)malloc(sizeof(Node)); 47 48 if (temp == NULL) 49 exit(EXIT_FAILURE); 50 temp->data = x; 51 52 /* 若s未被初始化 */ 53 if (s == NULL) 54 exit(EXIT_FAILURE); 55 temp->next = s->top; 56 s->top = temp; 57 s->size = (s->size) + 1; 58 } 59 60 /* 功能:出栈操作 61 * 输入:一个指向Stack型的指针 62 * 输出:被出栈的元素 63 */ 64 type 65 StackPop(Stack *s) 66 { 67 Node *p = NULL; 68 type r; 69 70 if (IsEmpty(s)) 71 exit(EXIT_FAILURE); 72 p = s->top->next; 73 r = s->top->data; 74 free(s->top); 75 s->top = p; 76 s->size = (s->size) - 1; 77 return r; 78 } 79 80 /* 功能:返回栈顶元素 81 * 输入:一个指向Stack型的指针 82 * 输出:栈顶元素 83 */ 84 type 85 GetTop(Stack *s) 86 { 87 if (IsEmpty(s)) 88 exit(EXIT_FAILURE); 89 return s->top->data; 90 }
1 #include <stdio.h> 2 #include "stack.h" 3 4 int main(int argc, char *argv[]) 5 { 6 Stack *s = StackInit(); 7 8 /* test passed 9 printf("s->top = %p\n", s->top); 10 printf("s->size = %u\n", s->size); 11 printf("s is empty: "); 12 IsEmpty(s) ? printf("yes\n") : printf("no\n"); 13 */ 14 15 /* test passed 16 StackPush(s, 'h'); 17 StackPush(s, 'e'); 18 StackPush(s, 'l'); 19 StackPush(s, 'l'); 20 StackPush(s, 'o'); 21 printf("s is empty: "); 22 IsEmpty(s) ? printf("yes\n") : printf("no\n"); 23 printf("s->size = %u\n", s->size); 24 25 while ((s->size) > 0) 26 { 27 printf("%c\n", StackPop(s)); 28 } 29 printf("s is empty: "); 30 IsEmpty(s) ? printf("yes\n") : printf("no\n"); 31 */ 32 33 /* test passed 34 StackPush(s, 'h'); 35 StackPush(s, 'e'); 36 StackPush(s, 'l'); 37 StackPush(s, 'l'); 38 StackPush(s, 'o'); 39 while (IsEmpty(s) == false) 40 { 41 printf("%c\n", GetTop(s)); 42 StackPop(s); 43 } 44 printf("s is empty: "); 45 IsEmpty(s) ? printf("yes\n") : printf("no\n"); 46 */ 47 48 // 栈的内存组织情况 - pass 49 /* 当s为空栈时内存组织情况 50 * p s - 0x602010 51 * p s->top - 0x0 52 * p s->size - 0 53 */ 54 55 StackPush(s, 'h'); 56 /* 当s中有一个值为'h'的元素时内存组织情况 57 * p s - 0x602010 58 * p s->top - 0x602030 59 * p s->size - 1 60 * p s->top->data - 'h' 61 * p s->top->next - 0x0 62 */ 63 64 StackPush(s, 'e'); 65 /* 当s中有两个元素时内存组织情况 66 * p s - 0x602010 67 * p s->top - 0x602050 68 * p s->size - 2 69 * p s->top->data - 'e' 70 * p s->top->next - 0x602030 71 * p s->top->next->data - 'h' 72 * p s->top->next->next - 0x0 73 */ 74 75 StackPop(s); 76 /* 当将栈顶元素弹出后内存组织情况 77 * p s - 0x602010 78 * p s->top - 0x602030 79 * p s->size - 1 80 * p s->top->data - 'h' 81 * p s->top->next - 0x0 82 */ 83 84 return 0; 85 }
注意:为了成功编译包含了stack.h的主程序,你需要使用下面的命令。也即必须将stack.c编译成目标文件,供链接程序链接使用,否则链接程序将报错。不同于C标准库的使用,因为C标准库默认采用动态链接的方式,所用标准库中的函数均已经被编译好了,放在特定的目录下面,链接程序只需去目录中找相对应的目标文件即可。这里我们必须将stack.c编译成目标文件,供主程序链接时使用。
gcc main.c stack.c
三 栈的一个应用实例
来做题吧!题目来源在此。需要说明的是下面的代码质量不好,因为代码是在上面的栈代码基础上拼凑而来的。为了记录每个字符在输入字符串中的位置,在栈结点中添加了一个index字段用于记录结点位置。算法原理很简单:将每一个字符入栈,判断入栈的字符是否为右括号(')'),如果是则将左右括号出栈并将二者在字符串中的位置按题目要求的顺序打印出来即可。
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <stdbool.h> 5 #define ARR_MAX_LEN 128 6 static char array[ARR_MAX_LEN]; 7 8 /* 数据类型 */ 9 typedef char type; 10 11 /* 栈结点 */ 12 typedef struct tsNode 13 { 14 int index; 15 type data; 16 struct tsNode *next; 17 } Node; 18 19 /* 栈类型 */ 20 typedef struct tsStack 21 { 22 Node *top; 23 unsigned int size; 24 } Stack; 25 26 /* 初始化Stack */ 27 Stack * 28 StackInit(void); 29 30 /* 判断栈是否为空。为空返回true,否则返回false */ 31 bool 32 IsEmpty(Stack *); 33 34 /* 入栈操作 */ 35 void 36 StackPush(Stack *, int, type); 37 38 /* 出栈操作 */ 39 type 40 StackPop(Stack *); 41 42 /* 取得栈顶元素 */ 43 Node * 44 GetTop(Stack *); 45 46 /* 功能:栈初始化 47 * 输入:无 48 * 输出:一个指向Stack型的指针,指针所指对象已被初始化 49 */ 50 Stack * 51 StackInit(void) 52 { 53 Stack *s = NULL; 54 55 s = (Stack *)malloc(sizeof(Stack)); 56 if (s == NULL) 57 exit(EXIT_FAILURE); 58 s->top = NULL; 59 s->size = 0; 60 return s; 61 } 62 63 /* 功能:判断栈是否为空。为空返回true,否则返回false 64 * 输入:一个指向Stack型的指针 65 * 输出:一个bool型变量 66 */ 67 bool 68 IsEmpty(Stack *s) 69 { 70 /* s未被初始化 */ 71 if (s == NULL) 72 exit(EXIT_FAILURE); 73 else 74 return ((s->size) == 0); 75 } 76 77 /* 功能:入栈操作 78 * 输入:一个指向Stack型的指针,一个type型待入栈变量 79 * 输出:无 80 */ 81 void 82 StackPush(Stack *s, int index, type x) 83 { 84 Node *temp = (Node *)malloc(sizeof(Node)); 85 86 if (temp == NULL) 87 exit(EXIT_FAILURE); 88 temp->index = index; 89 temp->data = x; 90 91 /* 若s未被初始化 */ 92 if (s == NULL) 93 exit(EXIT_FAILURE); 94 temp->next = s->top; 95 s->top = temp; 96 s->size = (s->size) + 1; 97 } 98 99 /* 功能:出栈操作 100 * 输入:一个指向Stack型的指针 101 * 输出:被出栈的元素 102 */ 103 type 104 StackPop(Stack *s) 105 { 106 Node *p = NULL; 107 type r; 108 109 if (IsEmpty(s)) 110 exit(EXIT_FAILURE); 111 p = s->top->next; 112 r = s->top->data; 113 free(s->top); 114 s->top = p; 115 s->size = (s->size) - 1; 116 return r; 117 } 118 119 /* 功能:返回栈顶元素 120 * 输入:一个指向Stack型的指针 121 * 输出:栈顶元素 122 */ 123 Node * 124 GetTop(Stack *s) 125 { 126 if (IsEmpty(s)) 127 exit(EXIT_FAILURE); 128 return s->top; 129 } 130 131 int 132 main(int argc, char *argv[]) 133 { 134 //freopen("in.txt", "r", stdin); 135 int i, len, temp; 136 Stack *s = StackInit(); 137 138 scanf("%s", array); 139 len = strlen(array); 140 for (i = 0; i < len; i++) 141 { 142 StackPush(s, i, array[i]); 143 if ((GetTop(s))->data == ')') 144 { 145 temp = GetTop(s)->index; 146 StackPop(s); 147 printf("%d %d\n", (GetTop(s))->index, temp); 148 StackPop(s); 149 } 150 } 151 152 //fclose(stdin); 153 return 0; 154 }
一个关于素数的神奇性质
一 写在前面
1.1 本文内容
一个关于素数的神奇性质。
二 素数性质
性质:所有大于等于5的素数一定和6的倍数相邻!此性质可以被证明,证明方法可以去搜索相关资料。下面给出1000以内的素数,你可以验证一下看是不是这样。
有了这个性质,下面再给出一个其在质因数分解中的实际应用例子。题目链接在此。题目大意是给定一个正整数N,要求将其分解成多个质因数相乘的形式。需要注意的是质因数分解的常用算法是试除法,例子中使用的也是试除法。并且每一个合数都能够写成几个质因数相乘的形式。
上述题目的一个可行代码如下:
1 #include <stdio.h> 2 #include <stdbool.h> 3 4 int main(int argc, char *argv[]) 5 { 6 //freopen("in.txt", "r", stdin); 7 int n, step; 8 bool first = true; 9 10 scanf("%d", &n); 11 12 /* 将数n中包含的2, 3, 5这三个质因数给除去 */ 13 while (n % 2 == 0) 14 { 15 (first == true) ? printf("2") : printf("*2"); 16 first = false; 17 n /= 2; 18 } 19 while (n % 3 == 0) 20 { 21 (first == true) ? printf("3") : printf("*3"); 22 first = false; 23 n /= 3; 24 } 25 while (n % 5 == 0) 26 { 27 (first == true) ? printf("5") : printf("*5"); 28 first = false; 29 n /= 5; 30 } 31 32 /* 让step从6开始,考察step相邻的两个数,如果能整除n,则将其除去 */ 33 step = 6; 34 while (n >= (step-1)) 35 { 36 if (n % (step-1) == 0) 37 { 38 (first == true) ? printf("%d", step-1) : printf("*%d", step-1); 39 first = false; 40 n /= (step-1); 41 } 42 else if (n % (step+1) == 0) 43 { 44 (first == true) ? printf("%d", step+1) : printf("*%d", step+1); 45 first = false; 46 n /= (step+1); 47 } 48 else 49 { 50 step += 6; 51 } 52 } 53 54 putchar('\n'); 55 //fclose(stdin); 56 return 0; 57 }
有人可能有疑问,虽然大于等于5的素数一定和6的倍数相邻,但反过来,所有与6的倍数相邻的却不一定是素数。也就是说在代码中的最后一个while循环中,step-1和step+1两个数可能是合数,那这样岂不是将n中的合数因子给除去了吗?事实果真是这样吗?不是的,前面说过,任何一个合数一定可以分解成几个质因数相乘的形式,也就是说即使step-1和step+1两个数中存在合数,那么它也无法整除n,因为在遇到该合数之前,该合数所能分解成的所有质因数都已经在n中给去除了。换句话说,在遇到n的合数因子(step-1或step+1)之前,该合数因子就因为其自身能被分解成更小的质因数而在之前的质因数分解中被分解殆尽了。所以上述代码是没问题的。
C# defualt关键字默认值用法
默认值表达式生成类型的默认值。 默认值表达式在泛型类和泛型方法中非常有用。 使用泛型类和泛型方法时出现的一个问题是,如何在无法提前知道以下内容的情况下将默认值赋值给参数化类型 T
:
-
T
是引用类型还是值类型。 - 如果
T
是值类型,它是数值还是用户定义的结构。
已知参数化类型 T
的变量 t
,仅当 T
为引用类型时,语句 t = null
才有效。 赋值 t = 0
仅对数值类型有效,对结构无效。 解决方案是使用默认值表达式,该表达式对引用类型(类类型和接口类型)返回 null
,对数值类型返回零。 对于用户定义的结构,返回初始化为零位模式的结构,该结构根据成员是值还是引用类型,为每个成员生成 0 或 null
。 对于可为 NULL 的值类型,default
返回像任何结构一样初始化的System.Nullable<T>。
default(T)
表达式不限于泛型类和泛型方法。 默认值表达式可用于任何托管类型
以上内容来自MSDN。
float fValue = default(float);
decimal dValue = default(decimal);
Console.Write("值类型测试:");
Console.WriteLine("{0},{1},{2}", iValue, fValue, dValue);
string strValue = default(string);
DateTime? dt = default(DateTime?);
User user = default(User);
Console.Write("引用类型测试:");
Console.WriteLine("{0},{1},{2}", (null == strValue), (null == user), (null == dt));
Console.WriteLine();
C# Json序列化和反序列化
{
/// <summary>
/// object转json字符串
/// </summary>
public static string ObjectToJson(this object obj)
{
if (null == obj)
{
return null;
}
return JsonConvert.SerializeObject(obj);
}
/// <summary>
/// json字符串转object
/// </summary>
public static T JsonToObject<T>(this string jsonString)
{
try
{
return JsonConvert.DeserializeObject<T>(jsonString);
}
catch
{
return default(T);
}
}
/// <summary>
/// 格式化json字符串
/// </summary>
public static string ConvertJsonString(this string str)
{
// 格式化json字符串
JsonSerializer serializer = new JsonSerializer();
TextReader tr = new StringReader(str);
JsonTextReader jtr = new JsonTextReader(tr);
object obj = serializer.Deserialize(jtr);
if (null != obj)
{
StringWriter textWriter = new StringWriter();
JsonTextWriter jsonWriter = new JsonTextWriter(textWriter)
{
Formatting = Formatting.Indented,
Indentation = 4,
IndentChar = ' '
};
serializer.Serialize(jsonWriter, obj);
return textWriter.ToString();
}
else
{
return str;
}
}
}
{
public static string GetJsonByObject<T>(this T obj)
{
if (null == obj)
{
return null;
}
//实例化DataContractJsonSerializer对象,需要待序列化的对象类型
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
//实例化一个内存流,用于存放序列化后的数据
using (MemoryStream stream = new MemoryStream())
{
//使用WriteObject序列化对象
serializer.WriteObject(stream, obj);
//写入内存流中
byte[] dataBytes = new byte[stream.Length];
stream.Position = 0;
stream.Read(dataBytes, 0, (int)stream.Length);
//通过UTF8格式转换为字符串
return Encoding.UTF8.GetString(dataBytes);
}
}
public static T GetObjectByJson<T>(this string jsonString)
{
try
{
//实例化DataContractJsonSerializer对象,需要待序列化的对象类型
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
//把Json传入内存流中保存
using (MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonString)))
{
// 使用ReadObject方法反序列化成对象
return (T)serializer.ReadObject(stream);
}
}
catch (Exception)
{
return default(T);
}
}
}
ASP.NET CORE系列【五】webapi整理以及RESTful风格化
介绍
什么是RESTful? 这里不多做赘述,详情请百度!
哈哈,本来还想巴拉巴拉介绍一些webapi, RESTful的, 还是算了,咱们直接上干货!(原因是懒!哈哈)
使用
以前使用过mvc的人对webapi 应该都很熟悉,先看一段熟悉的代码
大伙发现了什么没?跟以往mvc大多数相同,但有些地方不同 ,我们来一起看看有何区别
1.首先SysUsersController上面有一段代码
[Produces("application/json")] //[Route("api/SysUsers")] [Route("api/[controller]/[action]")] public class SysUsersController : Controller { }
注释的部分先不管,首先我们接收的json格式内容,然后路由是 api/[controller]/[action]
这种情况我们只需要正常的ajax请求就能访问到了,
$.ajax({ url:'/api/SysUsers/Login', type: 'POST', contentType: "application/json; charset=utf-8", data: {UserName:'shumin',Password:'123456'}, success: function (data) { if (data.success) { var href = '@Url.Action("Index")?' + new Date().getTime(); window.location.href = href; } else { alert(data.message); } }, error: function () { alert('服务端错误'); } });
我们运行一下, 发现拿不到数据,
这是为什么呢,经过多番尝试,是前台http请求不对,因为我们约定的json传输,所以数据需要序列化
$.ajax({ url:'/api/SysUsers/Login', type: 'POST', contentType: "application/json; charset=utf-8", data: JSON.stringify({UserName:'shumin',Password:'123456'}), success: function (data) { if (data.success) { var href = '@Url.Action("Index")?' + new Date().getTime(); window.location.href = href; } else { alert(data.message); } }, error: function () { alert('服务端错误'); } });
还有一点需要注意的事,ajax默认的
contentType是application/x-www-form-urlencoded
我们需要改成application/json; charset=utf-8
这样就可以拿到数据了
RESTful
推荐一个关于RESTful的介绍文章 http://www.ruanyifeng.com/blog/2014/05/restful_api
用上面那种方法,会导致URL又臭又长,举个例子
网站:/get_user?id=3
RESTFul: GET /user/3 (GET是HTTP类型)
以前的时候我们写http请求,只会用get post两种,
而注册对应的资源是user,api如下:
GET /user/:id # 获取id用户的信息
POST /user # 创建新的用户(注册)
PUT /user/:id # 更新id用户的信息
DELETE /user/:id # 删除id用户(注销)
我们一起来看看RESTFul API有哪些特点:
- 基于“资源”,数据也好、服务也好,在RESTFul设计里一切都是资源。
- 无状态。一次调用一般就会返回结果,不存在类似于“打开连接-访问数据-关闭连接”这种依赖于上一次调用的情况。
- URL中通常不出现动词,只有名词
- URL语义清晰、明确
- 使用HTTP的GET、POST、DELETE、PUT来表示对于资源的增删改查
使用JSON不使用XML
我们接着来看一看RESTFul API的一些最佳实践原则:
- 使用HTTP动词表示增删改查资源, GET:查询,POST:新增,PUT:更新,DELETE:删除
- 返回结果必须使用JSON
- HTTP状态码,在REST中都有特定的意义:200,201,202,204,400,401,403,500。比如401表示用户身份认证失败,403表示你验证身份通过了,但这个资源你不能操作。
这里直接贴出RESTful的api仅供参考
namespace NetCoreAdmin.Controllers { [Produces("application/json")] [Route("api/SysUsers")] public class SysUsersController : Controller { private readonly EFCoreContext _context; public SysUsersController(EFCoreContext context) { _context = context; } // GET: api/SysUsers [HttpGet] public IEnumerable<SysUser> GetSysUsers() { return _context.SysUsers; } // GET: api/SysUsers/5 [HttpGet("{id}")] public async Task<IActionResult> GetSysUser([FromRoute] int id) { if (!ModelState.IsValid) { return BadRequest(ModelState); } var sysUser = await _context.SysUsers.SingleOrDefaultAsync(m => m.Id == id); if (sysUser == null) { return NotFound(); } return Ok(sysUser); } // PUT: api/SysUsers/5 [HttpPut("{id}")] public async Task<IActionResult> PutSysUser([FromRoute] int id, [FromBody] SysUser sysUser) { if (!ModelState.IsValid) { return BadRequest(ModelState); } if (id != sysUser.Id) { return BadRequest(); } _context.Entry(sysUser).State = EntityState.Modified; try { await _context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException) { if (!SysUserExists(id)) { return NotFound(); } else { throw; } } return NoContent(); } // POST: api/SysUsers [HttpPost] public async Task<IActionResult> PostSysUser([FromBody] SysUser sysUser) { if (!ModelState.IsValid) { return BadRequest(ModelState); } _context.SysUsers.Add(sysUser); await _context.SaveChangesAsync(); return CreatedAtAction("GetSysUser", new { id = sysUser.Id }, sysUser); } // DELETE: api/SysUsers/5 [HttpDelete("{id}")] public async Task<IActionResult> DeleteSysUser([FromRoute] int id) { if (!ModelState.IsValid) { return BadRequest(ModelState); } var sysUser = await _context.SysUsers.SingleOrDefaultAsync(m => m.Id == id); if (sysUser == null) { return NotFound(); } _context.SysUsers.Remove(sysUser); await _context.SaveChangesAsync(); return Ok(sysUser); } private bool SysUserExists(int id) { return _context.SysUsers.Any(e => e.Id == id); } } }